一般认为彩虹有七种颜色构成,赤橙黄绿青蓝紫,然后是弧形。
显而易见,可以用 UIBezierPath
来绘制一条红色的扇形。
因为用的 iPhone7
的模拟器,所以可以把圆心设置在 180
的位置,半径 150
,绘制扇形,显然需要两条圆弧来完成,半径相差 10
。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(180, 250) radius:150 startAngle:M_PI endAngle:M_PI*2 clockwise:YES];
[bezierPath addArcWithCenter:CGPointMake(180, 250) radius:140 startAngle:0 endAngle:M_PI clockwise:NO];
[bezierPath closePath];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
layer.fillColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:layer];
这里需要注意的就是第二条圆弧两个点的顺序,因为第一条是从左到右绘制的,所以需要第二条从右向左绘制,因为 [bezierPath closePath];
这句代码是按顺序依次连接出现的各个顶点的。然后 clockwise:NO
反时钟方向绘制。
有了第一条,很容易得到下面的,当然需要创建多个 CALayer
,首先需要一个 colors
的数组,为了简化,都用 UIColor
固有的属性。
NSArray *colors = @[(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor orangeColor].CGColor,
(__bridge id)[UIColor yellowColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor cyanColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor purpleColor].CGColor];
上循环😆
for(int i=0; i<colors.count; i++) {
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(180, 250) radius:(150-10*i) startAngle:M_PI endAngle:M_PI*2 clockwise:YES];
[bezierPath addArcWithCenter:CGPointMake(180, 250) radius:(140-10*i) startAngle:0 endAngle:M_PI clockwise:NO];
[bezierPath closePath];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
layer.fillColor = (__bridge CGColorRef _Nullable)(colors[i]);
[self.view.layer addSublayer:layer];
}
我们得到了想要的彩虹🌈
简单实现,但是彩虹的颜色看起来很突兀,全都是色块叠加上去的,这显然不是很好的解决办法。
多重颜色渐变,很自然的会想到 CAGradientLayer
,作为 CALayer
的子类,它能轻松的实现多种颜色的组合渐变。
先绘简单的一条彩虹条线。
CAGradientLayer *layer = [CAGradientLayer layer];
layer.frame = CGRectMake(30, 130, 150, 1);
[self.view.layer addSublayer:layer];
layer.colors = colors;
layer.locations = @[@(1/7.0), @(2/7.0), @(3/7.0), @(4/7.0), @(5/7.0), @(6/7.0), @1.0];
layer.startPoint = CGPointMake(0, 0);
layer.endPoint = CGPointMake(1, 0);
locations
代表了每个色值绘制的区域范围,这里做了个平均,可以调整显示比例,而且 locations
的 count
必须要与 colors
的 count
保持一致,否则可能会得到空白。 startPoint
与 endPoint
两个参数代表了起始的坐标点,这里纵坐标不变都是 0
表示只做横向渐变,如果改成 layer.endPoint = CGPointMake(1, 1);
则会对角渐变。
发现彩虹属于弧形渐变,以某一圆点为中心的射线渐变,CAGradientLayer
属于矩形渐变,不能放射状展示渐变效果,对角渐变也无法得到我们想要的结果。
消耗性能的逐条绘制
for(int i=0; i<180; i++) {
CAGradientLayer *layer = [CAGradientLayer layer];
layer.frame = CGRectMake(50, 250, 70, 1);
[self.view.layer addSublayer:layer];
layer.colors = colors;
layer.locations = @[@(1/7.0), @(2/7.0), @(3/7.0), @(4/7.0), @(5/7.0), @(6/7.0), @1.0];
layer.startPoint = CGPointMake(0, 0);
layer.endPoint = CGPointMake(1, 0);
layer.affineTransform = CGAffineTransformMakeRotation(M_PI/180.0*i);
layer.anchorPoint = CGPointMake(1, 0.5);
}
我们得到了这样的图形
显然跟我们想要的图案相差甚远,首先是需要设置图像的锚点,因为想法是图像能绕一个圆点进行旋转,而 layer.anchorPoint
是一个[(0,0), (1,1)]
区间的值,表示视图从左下角到右上角的一个区间,显然无法设置到视图外的某点。 而且一个像素点逐行绘制会造成锯齿现象比较严重,
/* Defines the anchor point of the layer's bounds rect, as a point in
* normalized layer coordinates - '(0, 0)' is the bottom left corner of
* the bounds rect, '(1, 1)' is the top right corner. Defaults to
* '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. */
@property CGPoint anchorPoint;
想要使用这点,需要修改 colors
,手动加上白色或者透明色块,然后修改每一条的高度。
NSArray *colors = @[(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor orangeColor].CGColor,
(__bridge id)[UIColor yellowColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor cyanColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor purpleColor].CGColor,
(__bridge id)[UIColor clearColor].CGColor,
(__bridge id)[UIColor clearColor].CGColor];
for(int i=0; i<180; i++) {
CAGradientLayer *layer = [CAGradientLayer layer];
layer.frame = CGRectMake(100, 250, 150, 4);
[self.view.layer addSublayer:layer];
layer.colors = colors;
layer.locations = @[@(1/14.0), @(2/14.0), @(3/14.0), @(4/14.0), @(5/14.0), @(6/14.0), @0.5, @(8/14.0), @1];
layer.startPoint = CGPointMake(0, 0);
layer.endPoint = CGPointMake(1, 0);
layer.affineTransform = CGAffineTransformMakeRotation(M_PI/180.0*i);
layer.anchorPoint = CGPointMake(1, 0.5);
}
可以看出效果已经比之前平滑多了。
细心的朋友可能已经发现,为什么 [UIColor clearColor]
需要设置两次,因为渐变色是两个值之间发生的,如果只加一个,那么最后一段就变成了 [UIColor purpleColor]
到 [UIColor clearColor]
的渐变,看到的将会是紫色到透明的渐变,最后得到弧形很小,甚至是半圆。如果设置的是同色渐变,那么渐变效果就会失效。
还有一点就是图中的 frame.origin.x
设置了 100
但是模拟器中显示出来的显然没有这么多,这是因为修改了 layer
层的 anchorPoint
,修改视图的 anchorPoint
会造成视图的 bounds
修改。
/* The position in the superlayer that the anchor point of the layer's
* bounds rect is aligned to. Defaults to the zero point. Animatable. */
@property CGPoint position;
当你设置图层的frame属性的时候,
position
点的位置(也就是position
坐标)根据锚点(anchorPoint
)的值来确定,而当你设置图层的position
属性的时候,bounds
的位置(也就是frame
的orgin
坐标)会根据锚点(anchorPoint
)来确定。
根据上面说明,只需要在设置完视图的 layer.anchorPoint
之后,重新设置下 layer.frame
就可以解决视图位置问题了。
layer.anchorPoint = CGPointMake(1, 0.5);
layer.frame = CGRectMake(30, 250, 150, 4);
感谢: lvyong 在 彻底理解position与anchorPoint 提供的解决办法。