CoreGraphicsAndAnimation(1)
发表于|更新于
|阅读量:
绘图
成品展示

UIBezierPath
说到绘图,那么有一个不得不讲的对象,那就是UIBezierPath。先来看几个UIBezierPath里面比较重要的API。我都已经做了注释,功能还是比较强大的。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | + (instancetype)bezierPath;+ (instancetype)bezierPathWithRect:(CGRect)rect;
 + (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
 + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
 + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
 
 - (instancetype)init NS_DESIGNATED_INITIALIZER;
 
 - (void)moveToPoint:(CGPoint)point;
 - (void)addLineToPoint:(CGPoint)point;
 - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
 - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
 - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
 - (void)closePath;
 - (void)fill;
 - (void)stroke;
 - (void)addClip;
 
 @end
 
 | 
梯度
梯度的实现在ios里面有俩种方式,一种是通过CAGradientLayer去绘制,一种是通过CGGradientRef使用DrawRect:方法去绘制,我这里采用的是第二种方式。
实现方式,首先创建了一个CYShapeView的基类,该类可以设置一些属性值,如外边界色,里边界色,划线色,线宽等。方便子类继承它。通过设置的这些属性,可以获取到需要的梯度Ref指针。
下面是具体的代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 
 | @interface CYShapedView : UIView
 @property (nonatomic, strong) UIColor *innerColor;
 @property (nonatomic, strong) UIColor *outerColor;
 @property (nonatomic, strong) UIColor *strokeColor;
 @property (nonatomic, assign) CGFloat lineThickness;
 
 - (CGGradientRef)gradientWithColor:(UIColor *)c1
 toColor:(UIColor *)c2
 count:(CGFloat)colorNumb;
 @end
 
 @implementation CYShapedView
 
 - (id)initWithFrame:(CGRect)frame
 {
 self = [super initWithFrame:frame];
 if (self) {
 self.innerColor = [UIColor redColor];
 self.outerColor = [UIColor yellowColor];
 self.strokeColor = [UIColor blackColor];
 self.lineThickness = 3.f;
 self.backgroundColor = [UIColor clearColor];
 }
 return self;
 }
 
 - (void)setLineThickness:(CGFloat)lineThickness
 {
 CGFloat totalWidth = self.bounds.size.width;
 CGFloat totalHeight = self.bounds.size.height;
 CGFloat halfWidth = totalWidth / 2.f;
 CGFloat halfHeight = totalHeight / 2.f;
 CGFloat smallest = MIN(halfWidth, halfHeight);
 CGFloat factor = smallest / 100.f;
 factor = (factor > 1) ? 1 : factor;
 _lineThickness = factor * lineThickness;
 }
 
 - (CGGradientRef)gradientWithColor:(UIColor *)c1 toColor:(UIColor *)c2 count:(CGFloat)colorNumb
 {
 
 const CGFloat *colorComponents1 = CGColorGetComponents(c1.CGColor);
 CGFloat r1 = colorComponents1[0];
 CGFloat g1 = colorComponents1[1];
 CGFloat b1 = colorComponents1[2];
 
 const CGFloat *colorComponents2 = CGColorGetComponents(c2.CGColor);
 CGFloat r2 = colorComponents2[0];
 CGFloat g2 = colorComponents2[1];
 CGFloat b2 = colorComponents2[2];
 
 CGFloat gradientColorComponents[] = {
 r1,g1,b1,1,
 r2,g2,b2,1,
 r1,g1,b1,1,
 };
 
 
 CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
 
 CGGradientRef    gradientRef =  CGGradientCreateWithColorComponents(rgb, gradientColorComponents, NULL, colorNumb);
 CGColorSpaceRelease(rgb);
 
 return gradientRef;
 }
 @end
 
 | 
使用梯度及UIBezierPath绘制一个叶子
实现思路,创建一个继承自CYShapedView的子类,并将它命名为CYPetal。我们主要做的工作是在该类的DrawRect:方法里面
绘制一个叶子,首先要绘制的点有起点和终点,中间需要用曲线连接起来。那么接下来的工作就是找到这些点。
| 12
 3
 4
 5
 6
 
 | 1. 起点(startPoint):CGPointMake(self.view.bounds.size.width / 2, 3)2. 终点(endPoint):CGPointMake(self.view.bounds.size.width/ 2,self.view.bounds.size.height);
 3. 左叶子控制点(leftCtl):CGPointMake(-self.view.bounds.size.width / 2,self.view.bounds.size.height/ 3)
 4. 右叶子控制点(rightCtl):CGPointMake(self.view.bounds.size.width *1.5,self.view.bounds.size.height/ 3)
 5. 中间控制点(midPoint):CGPointMake(self.view.bounds.size.width / 2,self.view.bounds.size.height * 0.8)
 6. 边界点(corner):该点用来设置梯度的终点CGPointMake(self.view.bounds.size.width, 0)
 
 | 
下面来形象的展示下这些点的位置
以下是具体的实现代码,其实找到点之后,再用代码实现就很容易了。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | - (void)drawRect:(CGRect)rect {    CGFloat fullWidth = self.bounds.size.width;
 CGFloat fullHeight = self.bounds.size.height;
 CGFloat halfWidth = fullWidth / 2.f;
 CGFloat halfHeight = fullHeight / 2.f;
 CGPoint startPoint = CGPointMake(halfWidth, 3.f);
 CGPoint midPoint = CGPointMake(halfWidth, halfHeight * 1.6f);
 CGPoint endPoint = CGPointMake(halfWidth, fullHeight);
 CGPoint leftCtl = CGPointMake(-halfWidth, halfHeight / 3.f);
 CGPoint rightCtl = CGPointMake(fullWidth * 1.5f, halfHeight / 3.f);
 CGPoint corner = CGPointMake(fullWidth, 0);
 
 UIBezierPath *path = [UIBezierPath bezierPath];
 
 [path moveToPoint:startPoint];
 [path addCurveToPoint:endPoint controlPoint1:leftCtl controlPoint2:midPoint];
 [path addCurveToPoint:startPoint controlPoint1:midPoint controlPoint2:rightCtl];
 [path addClip];
 
 CGGradientRef gradient = [self gradientWithColor:self.innerColor toColor:self.outerColor count:3];
 CGContextRef context = UIGraphicsGetCurrentContext();
 CGContextDrawLinearGradient(context, gradient, CGPointZero, corner, 0);
 path.lineWidth = self.lineThickness;
 [self.strokeColor setStroke];
 [path stroke];
 }
 
 | 
贴个图感受下最后绘制出来的叶子

绘制笑脸
实现思路,同绘制叶子一样,我们先搞清楚需要的绘制参数。首先我们要绘制一个圆,用来显示脸。其次需要绘制一条曲线,用来显示嘴巴。最后还要绘制俩个椭圆用来显示眼睛。那么总结起来就是。
- 绘制圆,并做圆心向外发散的梯度处理
- 绘制曲线,俩个嘴角受中心点控制的贝塞尔曲线。
- 绘制椭圆,找到合适的位置调用贝塞尔的矩形框绘制椭圆路径即可。
实现的代码也比较简单,如下
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 
 | - (void)drawRect:(CGRect)rect {
 
 CGFloat fullWidth = self.bounds.size.width;
 CGFloat fullHeight = self.bounds.size.height;
 CGFloat halfWidth = fullWidth / 2.f;
 CGFloat halfHeight = fullHeight / 2.f;
 CGFloat radius = MIN(halfWidth, halfHeight);
 CGPoint center = CGPointMake(halfWidth, halfHeight);
 
 
 UIBezierPath *bezier = [UIBezierPath bezierPath];
 [bezier addArcWithCenter:center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
 [bezier addClip];
 
 
 CGGradientRef gradient = [self gradientWithColor:self.innerColor toColor:self.outerColor count:2];
 CGContextRef graphics = UIGraphicsGetCurrentContext();
 CGContextDrawRadialGradient(graphics, gradient, center, 0, center, radius, 0);
 
 
 bezier.lineWidth = self.lineThickness * 1.7;
 [self.strokeColor setStroke];
 [bezier stroke];
 
 
 [bezier removeAllPoints];
 bezier.lineWidth = self.lineThickness;
 [bezier moveToPoint:CGPointMake(halfWidth * 0.5, halfHeight * 1.3)];
 [bezier addQuadCurveToPoint:CGPointMake(halfWidth * 1.5, halfHeight * 1.3)
 controlPoint:CGPointMake(halfWidth, fullHeight * 0.91)];
 [bezier stroke];
 
 
 bezier = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(fullWidth / 3 - halfWidth * .1,
 fullHeight  / 3.f,
 halfWidth * .2,
 halfHeight * .3)];
 bezier.lineWidth = self.lineThickness;
 [bezier fill];
 [bezier stroke];
 
 
 bezier = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(fullWidth / 3 * 2 - halfWidth * .1,
 fullHeight / 3.f,
 halfWidth * .2,
 halfHeight * .3)];
 bezier.lineWidth = self.lineThickness;
 [bezier fill];
 [bezier stroke];
 }
 
 | 
按照惯例,贴下实现的效果图

动起来
现在我们希望能用之前绘制的俩个图形,集合动画,做出一个类似太阳的动画特效。
还是谈谈实现的思路:先用之前的CYSmile绘制一个太阳,之后采用CYPetal绘制一组阳光,阳光的绘制有技巧,需要绘制相同frame的光芒视图15个,之后采用transform的方式,设置位置偏移。此外由于要实现动画效果,需要对光芒的锚点位置进行设置。以下是一个view设置的时候,太阳和光芒的位置设置。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | ----------------------------------------------------------------------|                                                                     |
 |                                                                     |
 |                                                                     |
 |                -------------------------------                      |
 |                |         -------             |                      |
 |                |         |     |             |                      |
 |                |         |  光 |             |                      |
 |                |         |  芒 |             |                      |
 |                |         |  区 |             |                      |
 |                |         |     |             |                      |
 |                |         |     |             |                      |
 |                |         -------             |                      |
 |                |                             |                      |
 |                |         太阳区              |                      |
 |                |                             |                      |
 |                -------------------------------                      |
 |                                                                     |
 |                                                                     |
 |                                                                     |
 |                                                                     |
 ------------------------------------------------------------------------
 
 | 
具体的话还是看下代码吧。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 
 | - (void)createSunshine{
 
 
 CGFloat fullWidth = self.bounds.size.width;
 CGFloat fullHeight = self.bounds.size.height;
 CGFloat halfWidth = fullWidth / 2.f;
 CGFloat halfHeigt = fullHeight / 2.f;
 
 
 CGRect sunRect = CGRectMake(halfWidth - fullWidth / 4, halfHeigt - fullHeight / 4, halfWidth, halfHeigt);
 CGRect sunshineRect = CGRectMake(halfWidth - fullWidth / 40, halfHeigt - fullHeight / 8, fullWidth / 20, fullHeight / 4);
 
 
 UIView *shineView = [[UIView alloc]initWithFrame:self.bounds];
 self.shineLayer = shineView.layer;
 [self addSubview:shineView];
 
 
 for (CGFloat i = M_PI / 10; i < M_PI * 2; i += M_PI / 7.5) {
 CYPetal *petal = [[CYPetal alloc]initWithFrame:sunshineRect];
 petal.innerColor = [UIColor yellowColor];
 petal.outerColor = [UIColor colorWithRed:1 green:0.8 blue:.2 alpha:1];
 petal.lineThickness = 40.f;
 petal.strokeColor = [UIColor whiteColor];
 [shineView addSubview:petal];
 
 petal.layer.anchorPoint = CGPointMake(.5, 2);
 petal.transform = CGAffineTransformMakeRotation(i);
 }
 
 
 [self addRotationAnimation];
 
 
 CYSmile *smile = [[CYSmile alloc]initWithFrame:sunRect];
 smile.innerColor = [UIColor yellowColor];
 smile.outerColor = [UIColor colorWithRed:1 green:.8 blue:.2 alpha:1];
 [self addSubview:smile];
 
 }
 
 
 
 - (void)addRotationAnimation
 {
 CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
 
 
 basicAnimation.duration = 10;
 basicAnimation.fromValue = @0;
 basicAnimation.speed = self.speed;
 basicAnimation.toValue = @(M_PI * 2);
 basicAnimation.repeatCount = CGFLOAT_MAX;
 [self.shineLayer addAnimation:basicAnimation forKey:@"rotation"];
 
 
 basicAnimation.keyPath = @"opacity";
 basicAnimation.duration = .5f;
 basicAnimation.autoreverses = YES;
 basicAnimation.fromValue = @0.7;
 basicAnimation.toValue = @1.0;
 [self.shineLayer addAnimation:basicAnimation forKey:@"fade"];
 
 
 basicAnimation.keyPath = @"transform.scale";
 basicAnimation.fromValue = @0.9;
 basicAnimation.toValue = @1.1;
 [self.shineLayer addAnimation:basicAnimation forKey:@"scale"];
 
 }
 
 | 
最后的效果图如下

组合绘制花朵
花朵包括的元素主要有笑脸、花瓣、茎、叶子。这些元素(除了茎)都可以使用之前的组件来绘制出来。
茎
坐标路径大概如下,并不一定是这样,手工绘制敬请谅解
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 | 
茎的绘制和之前花瓣的绘制差不多,都是要选择一个起点和一个终点,然后绘制根据俩个锚点,绘制出相应的图形,具体绘制就不细说了,还是附上代码感受下吧
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | - (void)drawRect:(CGRect)rect{
 CGFloat fullWidth = self.bounds.size.width;
 CGFloat fullHeight = self.bounds.size.height;
 CGFloat halfWidth = fullWidth / 2;
 CGFloat halfHeight = fullHeight / 2;
 
 CGPoint startPoint = CGPointMake(halfWidth * .9, 0);
 CGPoint midPoint = CGPointMake(halfWidth, halfHeight);
 CGPoint endPoint = CGPointMake(halfWidth * .9, fullHeight);
 CGPoint corner = CGPointMake(fullWidth, 0);
 CGPoint leftCtl = CGPointMake(0, halfHeight);
 CGPoint rightCtl = CGPointMake(fullWidth, fullHeight);
 
 UIBezierPath *path = [UIBezierPath bezierPath];
 [path moveToPoint:startPoint];
 [path addCurveToPoint:endPoint controlPoint1:leftCtl controlPoint2:rightCtl];
 [path addCurveToPoint:startPoint controlPoint1:rightCtl controlPoint2:midPoint];
 [path closePath];
 [path addClip];
 
 CGContextRef ref = UIGraphicsGetCurrentContext();
 CGGradientRef gradient = [self gradientWithColor:self.innerColor toColor:self.outerColor count:3];
 CGContextDrawLinearGradient(ref, gradient, CGPointZero, corner, 0);
 path.lineWidth = self.lineThickness;
 
 [self.strokeColor setStroke];
 [path stroke];
 
 
 }
 
 | 
组合花朵
组合花朵,主要就是在于计算笑脸、茎、花瓣、叶子等的坐标位置。然后还有就是设置它们的颜色。此外要注意设置花朵的frame的时候,要保持宽高比为7 : 10,这样才能生成比较好的效果。
下面附上花朵的位置设置代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | - (id)initWithFrame:(CGRect)frame{
 self = [super initWithFrame:frame];
 if (self) {
 [self p_createFlower];
 }
 return self;
 }
 
 - (void)p_createFlower
 {
 
 CGFloat fullwidth = self.bounds.size.width;
 CGFloat fullHeight = self.bounds.size.height;
 CGFloat halfWidth = self.bounds.size.width/2.f;
 CGFloat halfHeight = self.bounds.size.height/2.f;
 
 
 CGRect smileRect = CGRectMake(halfWidth/2, halfHeight/4*.9,
 halfWidth, halfHeight);
 CGRect petalRect = CGRectMake(halfWidth-fullwidth/10, fullHeight/5,
 fullwidth/5, fullwidth/2);
 CGRect leafRect = CGRectMake(halfWidth-fullwidth/12, fullHeight*.84,
 fullwidth/5, fullwidth/2);
 CGRect stemRect = CGRectMake(halfWidth-fullwidth/8, halfHeight*1.3,
 fullwidth/4, halfHeight*.8);
 
 CYStem *stem = [[CYStem alloc]initWithFrame:stemRect];
 stem.outerColor = [UIColor colorWithRed:0 green:0.5 blue:0 alpha:1];
 stem.innerColor = [UIColor colorWithRed:.3 green:1 blue:.2 alpha:1];
 [self addSubview:stem];
 
 
 for (CGFloat i = M_PI / 10; i < M_PI * 2; i += M_PI / 7.5) {
 CYPetal *petal = [[CYPetal alloc]initWithFrame:petalRect];
 petal.outerColor = [UIColor  purpleColor];
 petal.innerColor = [UIColor colorWithRed:1 green:0 blue:1 alpha:1];
 [self addSubview:petal];
 petal.layer.anchorPoint = CGPointMake(.5, 1);
 petal.transform = CGAffineTransformMakeRotation(i);
 }
 
 
 for (CGFloat i = -M_PI/5; i <= M_PI/5; i += M_PI * 2/5) {
 CYPetal *leaf = [[CYPetal alloc]initWithFrame:leafRect];
 leaf.outerColor = [UIColor colorWithRed:0 green:.5 blue:0 alpha:1 ];
 leaf.innerColor = [UIColor colorWithRed:.3 green:1 blue:.2 alpha:1];
 [self addSubview:leaf];
 leaf.layer.anchorPoint = CGPointMake(.5, 1);
 leaf.transform = CGAffineTransformMakeRotation(i);
 }
 
 
 CYSmile *smile = [[CYSmile alloc]initWithFrame:smileRect];
 smile.innerColor = [UIColor yellowColor];
 smile.outerColor = [UIColor colorWithRed:1 green:.4 blue:0 alpha:1];
 [self addSubview:smile];
 
 }
 
 | 

参考资料