渐变圆环实现记录

先了解下坐标系
iOS layer坐标系
position属性是决定子layer在父layer上的位置,默认为(0,0)。其次,anchorPoint属性是决定子layer上的哪个点会在position所指定的位置。

最近需求里实现一个灯光亮度、开关效果
关灯状态
开灯状态,5级亮度(环个数代表亮度级数

代码示例:VSColorWithHexString为16进制颜色值转UIColor的宏。
.h文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, VSSceneControlViewType){
VSSceneControlViewTypeOn = 1,
VSSceneControlViewTypeOff = 2
};
@interface VSSceneControlView : UIView
@property (nonatomic, strong) UIColor *offColor;
@property (nonatomic, strong) UIColor *startColor;
@property (nonatomic, strong) UIColor *endColor;
@property (nonatomic, assign) NSInteger layerCount; //光圈个数,用于计算渐变等级,默认为5

- (instancetype)initWithRadius:(CGFloat)radius borderWidth:(CGFloat)borderWidth;
- (void)controlOnWithRingCount:(NSInteger)ringCount; //开灯亮度
- (void)controlOff; //关闭
@end

.m实现文件
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#import "VSSceneControlView.h"

@interface VSSceneControlView()
@property (nonatomic, strong) UIImageView *centerIcon;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, assign) CGFloat borderWidth;
@property (nonatomic, assign) VSSceneControlViewType type;
@property (nonatomic, strong) NSArray *layerArray;
@end

@implementation VSSceneControlView

- (instancetype)initWithRadius:(CGFloat)radius borderWidth:(CGFloat)borderWidth {
if(self = [super init]) {
_radius = radius;
_type = VSSceneControlViewTypeOff;
_borderWidth = borderWidth;
_layerCount = 5;
_startColor = VSColorWithHexString(@"FA9D24");
_endColor = VSColorWithHexString(@"FCD96A");
_offColor = VSColorWithHexString(@"F4F4F4");
[self commonInit];
}
return self;
}
- (void)commonInit{
_centerIcon = [[UIImageView alloc] init];
_centerIcon.contentMode = UIViewContentModeScaleAspectFill;
[self addSubview:_centerIcon];
[_centerIcon mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(@0);
make.width.equalTo(@57);
make.height.equalTo(@57);
}];

}
- (void)clearLayers {
for (CALayer *layer in self.layerArray) {
[layer removeAllAnimations];
[layer removeFromSuperlayer];
}
}
- (void)controlOnWithRingCount:(NSInteger)ringCount {
[self clearLayers];
_centerIcon.image = [UIImage imageNamed:@"scene_on"];
NSMutableArray *layers = [NSMutableArray array];
NSInteger count = (ringCount > self.layerCount) ? self.layerCount : ringCount;
count = (count == 0) ? 1 : count;
NSArray<NSNumber *> *opacitys = @[@1,@0.5,@0.3,@0.15,@0.06];
for (int i = 1 ;i <= count; i++) {
CAShapeLayer *ringLayer = [[CAShapeLayer alloc] init];
ringLayer.frame = self.bounds;
ringLayer.fillColor = [UIColor clearColor].CGColor;
ringLayer.strokeColor = self.offColor.CGColor;
ringLayer.lineCap = kCALineCapButt;
ringLayer.lineJoin = kCALineJoinRound;
ringLayer.lineWidth = self.borderWidth;
ringLayer.strokeStart = 0;
ringLayer.strokeEnd = 1;
CGFloat radius = (self.radius + (i-1) * _borderWidth);
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.radius, self.radius) radius:radius startAngle:0 endAngle:2 * M_PI clockwise:NO];
ringLayer.path = path.CGPath;

CALayer *layer = [[CALayer alloc] init];
layer.frame = self.bounds;
[layer setMask:ringLayer];

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(0, 0, (radius + _borderWidth) * 2 , (radius + _borderWidth) * 2);
gradientLayer.position = CGPointMake(self.radiu, self.radius);
// 颜色分配
[gradientLayer setColors:[NSArray arrayWithObjects:(id)self.startColor.CGColor,(id)self.endColor.CGColor, nil]];
gradientLayer.opacity = [opacitys[i-1] floatValue];
[gradientLayer setLocations:@[@0.5]]; // 颜色分割线
[gradientLayer setStartPoint:CGPointMake(0, 0)]; //起点
[gradientLayer setEndPoint:CGPointMake(0, 1)]; //结束点 该效果从上垂直向下渐变,(0,0)-(1,1)则是左上斜向右下
[layer addSublayer:gradientLayer];

[self.layer addSublayer:layer];
[layers addObject:layer];
}
self.layerArray = [layers copy];
_type = VSSceneControlViewTypeOn;
}
- (void)controlOff {
[self clearLayers];
_centerIcon.image = [UIImage imageNamed:@"scene_off"];
CAShapeLayer *offLayer = [[CAShapeLayer alloc] init];
offLayer.frame = self.bounds;
offLayer.fillColor = [UIColor clearColor].CGColor;
offLayer.strokeColor = self.offColor.CGColor;
offLayer.lineCap = kCALineCapButt;
offLayer.lineJoin = kCALineJoinRound;
offLayer.lineWidth = self.borderWidth;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.radius, self.radius) radius:self.radius startAngle:0 endAngle:2 * M_PI clockwise:NO];
offLayer.path = path.CGPath;
[self.layer addSublayer:offLayer];

NSMutableArray *layers = [NSMutableArray array];
[layers addObject:offLayer];
self.layerArray = [layers copy];
_type = VSSceneControlViewTypeOn;
}
@end