弧形渐变效果图

在iOS中,经常会需要实现一些渐变的效果,我们知道这个一般使用CAGradientLayer来实现,但是这里只能实现单方向的平滑过渡,如从左至右,从左上到左下等,但是想实现一个弧形的渐变效果时,则需要进行多个方向的渐变效果,这里要怎么实现呢?
先祭上最终的效果图:
效果图

CAShapeLayer

基本属性设置

1
2
3
4
5
6
7
8
9
10
11
12
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [UIBezierPath
bezierPathWithArcCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5 + 110)
radius:120
startAngle:(150.f * M_PI) / 180.f
endAngle:(390.f * M_PI) / 180.f
clockwise:1].CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor lightGrayColor].CGColor;
shapeLayer.lineWidth = 15.f;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.opacity = 0.8;

属性介绍

strokeStart属性和strokeEnd属性

strokeStartstrokeEnd是一个0-1的取值范围。表示一段路径的开始和结尾。比如开始位置为0.5,结束位置为1,那就只渲染出后半段的路径。
strokeStart 翻译过来就是清除开始位置。
strokeEnd 意思就是清除结束的位置。

  • keyPath = strokeStart 动画的fromValue = 0,toValue = 1
    表示从路径的0位置画到1,怎么画是按照清除开始的位置也就是清除0,一直清除到1,效果就是一条路径慢慢的消失。
  • keyPath = strokeStart 动画的fromValue = 1,toValue = 0
    表示从路径的1位置画到0,怎么画是按照清除开始的位置也就是1,这样开始的路径是空的(即都被清除掉了)一直清除到0,效果就是一条路径被反方向画出来。
  • keyPath = strokeEnd 动画的fromValue = 0,toValue = 1
    表示这里我们分3个点说明动画的顺序,strokeEnd从结尾开始清除,首先整条路径先清除后2/3,接着清除1/3,效果就是正方向画出路径。
  • keyPath = strokeEnd 动画的fromValue = 1,toValue = 0
    效果就是反方向路径慢慢消失。

那么我们就可以利用strokeStartstrokeEnd这两个属性,对CAShapeLayer进行添加动画,在设置percent时,进度条就会出现增大,和减少的两种动态效果。

1
2
3
4
5
6
- (void)setPercent:(CGFloat)percent {
_shapeLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5 + 110) radius:120 startAngle:(150.f * M_PI) / 180.f endAngle:((240.f * (percent - 150.f) / 800.f + 150.f) * M_PI) / 180.f clockwise:1].CGPath;
CGFloat value = (percent - 150.f) / 800.f; //计算进度条的位置点
_shapeLayer.strokeStart = MIN(0, value);
_shapeLayer.strokeEnd = MAX(0, value);
}

CAGradientLayer

基本属性设置,并给CAShapeLayer添加渐变色。做成的整体的进度条效果。

1
2
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
72
73
74
75
76
77
78
79
80
81
82
83
84
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [UIBezierPath
bezierPathWithArcCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5 + 110)
radius:120
startAngle:(150.f * M_PI) / 180.f
endAngle:(390.f * M_PI) / 180.f
clockwise:1].CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor lightGrayColor].CGColor;
shapeLayer.lineWidth = 15.f;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.opacity = 0.8;
_shapeLayer = shapeLayer;
//上方层
CAGradientLayer *gradientLayer1 = [CAGradientLayer layer];
gradientLayer1.frame = self.frame;
gradientLayer1.colors = self.colorsTop;
gradientLayer1.startPoint = CGPointMake(0, 0);
gradientLayer1.endPoint = CGPointMake(1, 0);
gradientLayer1.mask = shapeLayer;

//左下层
CAGradientLayer *gradientLayer2 = [CAGradientLayer layer];
gradientLayer2.frame = CGRectMake(0, SHEIGHT / 2.f + circleRadius / 3.f , SWIDTH / 2, circleRadius * 1.3);
gradientLayer2.colors = self.colorsLeftBottom;
gradientLayer2.startPoint = CGPointMake(0, 1);
gradientLayer2.endPoint = CGPointMake(0, 0);

//右下层
CAGradientLayer *gradientLayer3 = [CAGradientLayer layer];
gradientLayer3.frame = CGRectMake(SWIDTH / 2, SHEIGHT / 2.f + circleRadius / 3.f , SWIDTH / 2, circleRadius * 1.3);
gradientLayer3.colors = self.colorsRightBottom;
gradientLayer3.startPoint = CGPointMake(0, 0);
gradientLayer3.endPoint = CGPointMake(0, 1);

[self.layer addSublayer:gradientLayer1];
[gradientLayer1 addSublayer:gradientLayer2];
[gradientLayer1 addSublayer:gradientLayer3];
[gradientLayer1 setLocations:@[@0.35,@0.5,@0.75]];
[gradientLayer3 setLocations:@[@0.2,@0.5,@0.75]];
[gradientLayer2 setLocations:@[@0.2,@0.5,@0.75]];
}
return self;
}

- (NSMutableArray *)colorsTop {
if (!_colorsTop) {
_colorsTop = [NSMutableArray array];
[_colorsTop addObject:(__bridge id)RGB(0xe2, 0xf2, 0x69).CGColor];
[_colorsTop addObject:(__bridge id)RGB(0xff, 0xcf, 0x57).CGColor];
[_colorsTop addObject:(__bridge id)RGB(0xff, 0xa8, 0x4c).CGColor];
}
return _colorsTop;
}

- (NSMutableArray *)colorsLeftBottom {
if (!_colorsLeftBottom) {
_colorsLeftBottom = [NSMutableArray array];
[_colorsLeftBottom addObject:(__bridge id)RGB(0x73, 0xf2, 0x92).CGColor];
[_colorsLeftBottom addObject:(__bridge id)RGB(0xab, 0xf2, 0x6e).CGColor];
[_colorsLeftBottom addObject:(__bridge id)RGB(0xe2, 0xf2, 0x69).CGColor];
}
return _colorsLeftBottom;
}

- (NSMutableArray *)colorsRightBottom {
if (!_colorsRightBottom) {
_colorsRightBottom = [NSMutableArray array];
[_colorsRightBottom addObject:(__bridge id)RGB(0xff, 0xa8, 0x4c).CGColor];
[_colorsRightBottom addObject:(__bridge id)RGB(0xff, 0x80, 0x4c).CGColor];
[_colorsRightBottom addObject:(__bridge id)RGB(0xe2, 0x56, 0x4c).CGColor];
}
return _colorsRightBottom;
}

- (void)setPercent:(CGFloat)percent {
// _shapeLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5 + 110) radius:120 startAngle:degreesToRadians(150.f) endAngle:degreesToRadians(percent / 200.f * 240.f + 150.f) clockwise:1].CGPath;
CGFloat value = percent / 200.f; //计算进度条的位置点
_shapeLayer.strokeStart = MIN(0, value);
_shapeLayer.strokeEnd = MAX(0, value);
}

关于CABasicAnimation,使用strokeStartstrokeEnd做动画时,可以不添加这个animation,也是有动画效果的,在进度条偶尔增大偶尔减少时我觉得用属性设置的方式比较好。
关于设置渐变色的位置,在未添加gradientLayermask的效果如图,这样可以更清楚的看到,三种颜色过渡方式,上方为从左向右进行过渡,左下方为从下到上进行过渡,右下方为从上到下进行过渡,看到整体效果后,再去添加mask,就可以实现一个平滑的弧形过渡啦。

未添加贝塞尔路径的效果图