iOS核⼼动画详解(CABasicAnimation)
前⾔
  上⼀篇已经介绍了核⼼动画在UI渲染中的位置和基本概念,但是没有具体介绍CAAnimation⼦类的⽤法,本⽂将介绍CABasicAnimation及其⼦类CASpringAnimation 的⽤法和⼀些注意事项。
⼀、CABasicAnimation
1.什么是CABasicAnimation
  CABasicAnimation是核⼼动画类簇中的⼀个类,其⽗类是CAPropertyAnimation,其⼦类是CASpringAnimation,它的祖⽗是CAAnimation。它主要⽤于制作⽐较单⼀的动画,例如,平移、缩放、旋转、颜⾊渐变、边框的值的变化等,也就是将layer的某个属性值从⼀个值到另⼀个值的变化。类似x -> y这种变化,然⽽对于x -> y -> z 甚⾄更多的变化是不⾏的。
2.常⽤属性
  @property(nullable, strong) id fromValue;
  @property(nullable, strong) id toValue;
  @property(nullable, strong) id byValue;
  很明显,fromvalue表⽰初始状态,tovalue表⽰最终状态,byvalue是在fromvalue的基础上发⽣的变化,这个可以慢慢测试,主要还是from和to。
3.实例化⽅法
  + (instancetype)animationWithKeyPath:(nullable NSString *)path;
  下⾯是对keypath⽐较全⾯的总结,每个keypath对应⼀种属性值的变化,其中涉及到颜⾊变化的都必须使⽤CGColor,因为对应是对layer的属性。
1 #ifndef AnimationKeyPathName_h
2#define AnimationKeyPathName_h
3#import <Foundation/Foundation.h>
4/* CATransform3D Key Paths */
5/* 旋转x,y,z分别是绕x,y,z轴旋转 */
6static NSString *kCARotation = @"ation";
7static NSString *kCARotationX = @"ation.x";
8static NSString *kCARotationY = @"ation.y";
9static NSString *kCARotationZ = @"ation.z";
10
11/* 缩放x,y,z分别是对x,y,z⽅向进⾏缩放 */
12static NSString *kCAScale = @"transform.scale";
13static NSString *kCAScaleX = @"transform.scale.x";
14static NSString *kCAScaleY = @"transform.scale.y";
15static NSString *kCAScaleZ = @"transform.scale.z";
16
html animation属性
17/* 平移x,y,z同上 */
18static NSString *kCATranslation = @"anslation";
19static NSString *kCATranslationX = @"anslation.x";
20static NSString *kCATranslationY = @"anslation.y";
21static NSString *kCATranslationZ = @"anslation.z";
22
23/* 平⾯ */
24/* CGPoint中⼼点改变位置,针对平⾯ */
25static NSString *kCAPosition = @"position";
26static NSString *kCAPositionX = @"position.x";
27static NSString *kCAPositionY = @"position.y";
28
29/* CGRect */
30static NSString *kCABoundsSize = @"bounds.size";
31static NSString *kCABoundsSizeW = @"bounds.size.width";
32static NSString *kCABoundsSizeH = @"bounds.size.height";
33static NSString *kCABoundsOriginX = @"igin.x";
34static NSString *kCABoundsOriginY = @"igin.y";
35
36/* 透明度 */
37static NSString *kCAOpacity = @"opacity";
38/* 背景⾊ */
39static NSString *kCABackgroundColor = @"backgroundColor";
40/* 圆⾓ */
41static NSString *kCACornerRadius = @"cornerRadius";
42/* 边框 */
43static NSString *kCABorderWidth = @"borderWidth";
44/* 阴影颜⾊ */
45static NSString *kCAShadowColor = @"shadowColor";
46/* 偏移量CGSize */
47static NSString *kCAShadowOffset = @"shadowOffset";
48/* 阴影透明度 */
49static NSString *kCAShadowOpacity = @"shadowOpacity";
50/* 阴影圆⾓ */
51static NSString *kCAShadowRadius = @"shadowRadius";
52#endif /* AnimationKeyPathName_h */
4.简单⽤法
  下⾯的类是⽤来测试不同keypath对应的动画,有些省略了,因为原理都是⼀样的。
  #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
  #define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
1#import"BaseAnimationView.h"
2
3@interface BaseAnimationView()<UITableViewDelegate,UITableViewDataSource>
4 @property (nonatomic, strong) UIView *squareView;
5 @property (nonatomic, strong) UILabel *squareLabel;
6 @property (nonatomic, strong) UITableView *tableView;
7 @property (nonatomic, strong) NSMutableArray *dataSource;
8@end
9
10@implementation BaseAnimationView
11
12 - (instancetype)init{
13if (self = [super initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]) {
14        [self initData];
15        [self setupUI];
16    }
17return self;
18 }
19 - (void)setupUI{
20    self.squareView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 80)];
21    self.squareView.backgroundColor = [UIColor cyanColor];
22    self.squareView.layer.borderColor = [UIColor redColor].CGColor;
23    = CGPointMake(SCREEN_WIDTH/2.0, 200);
24    self.squareView.layer.shadowOpacity = 0.6;
25    self.squareView.layer.shadowOffset = CGSizeMake(0, 0);
26    self.squareView.layer.shadowRadius = 4;
27    self.squareView.layer.shadowColor = [UIColor redColor].CGColor;
28    [self addSubview:self.squareView];
29
30    self.squareLabel = [[UILabel alloc] initWithFrame:self.squareView.bounds];
31    = @"label";
32    Alignment = NSTextAlignmentCenter;
33    Color = [UIColor blackColor];
34    self.squareLabel.font = [UIFont systemFontOfSize:17];
35    [self.squareView addSubview:self.squareLabel];
36
37    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 400, SCREEN_WIDTH, SCREEN_HEIGHT-400) style:UITableViewStylePlain];
38    self.tableView.delegate = self;
39    self.tableView.dataSource = self;
40    [self addSubview:self.tableView];
41 }
42
43 - (CABasicAnimation *)getAnimationKeyPath:(NSString *)keyPath fromValue:(id)fromValue toValue:(id)toValue{
44    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:keyPath];
45    basicAnimation.fromValue = fromValue;
46/*byvalue是在fromvalue的值的基础上增加量*/
47//basicAnimation.byValue = @1;
48    Value = toValue;
49    basicAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];;
50    basicAnimation.duration = 2;
51    peatCount = 1;
52/* animation remove from view after animation finish */
53    vedOnCompletion = YES;
54return basicAnimation;
55 }
56 - (void)initData{
57/*
58    kCAScaleZ 缩放z 没有意义,因为是平⾯图形
59    kCAPositionX设置y没有意义,可以随意设置,同理kCAPositionY设置x没有意义
60    kCABackgroundColor,颜⾊变化必须要⽤CGColor
61⽤到shadow的⼏个属性变化的时候,需要先设置shadow
62*/
63    NSValue *startPoint = [NSValue valueWithCGPoint:];
64    NSValue *endPoint = [NSValue valueWithCGPoint:CGPointMake(500, 500)];
65    NSValue *shadowStartPoint = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
66    NSValue *shadowEndPoint = [NSValue valueWithCGPoint:CGPointMake(5, 5)];
67id startColor = (id)([UIColor cyanColor].CGColor);
68id endColor = (id)([UIColor redColor].CGColor);
69id shadowStartColor = (id)[UIColor clearColor].CGColor;
70id shadowEndColor = (id)[UIColor redColor].CGColor;
71    self.dataSource = [NSMutableArray array];
72    NSArray *keypaths  = @[kCARotation,kCARotationX,kCARotationY,kCARotationZ,
73                            kCAScale,kCAScaleX,kCAScaleZ,kCAPositionX,
74                            kCABoundsSizeW,kCAOpacity,kCABackgroundColor,kCACornerRadius,
75                            kCABorderWidth,kCAShadowColor,kCAShadowRadius,kCAShadowOffset];
76
77    NSArray *fromValues = @[@0,@0,@0,@0,
78                            @0,@0,@0,startPoint,
79                            @100,@1,startColor,@0,
80                            @0,shadowStartColor,@0,shadowStartPoint];
81
82    NSArray *toValues  = @[@(M_PI),@(M_PI),@(M_PI),@(M_PI),
83                            @1,@1,@1,endPoint,
84                            @200,@0,endColor,@40,
85                            @4,shadowEndColor,@8,shadowEndPoint];
86for (int i=0; i&unt; i++) {
87        AnimationModel *model = [[AnimationModel alloc] init];
88        model.keyPaths = keypaths[i];
89        model.fromValue = fromValues[i];
90        Value = toValues[i];
91        [self.dataSource addObject:model];
92    }
93 }
94 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
95return1;
96 }
97 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
98return [self.dataSource count];
99 }
100 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
101    AnimationModel *model = [self.dataSource w];
102    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellID"];
103if (cell==nil) {
104        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellID"];
105    }
106    = model.keyPaths;
107    cell.selectionStyle = 0;
108return cell;
109 }
110 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
111    AnimationModel *model = [self.dataSource w];
112    CABasicAnimation *animation = [self getAnimationKeyPath:model.keyPaths fromValue:model.fromValue Value];
113    [self.squareView.layer addAnimation:animation forKey:nil];
114 }
115@end
  之所以在执⾏动画的view上加⼊⼀个label,是为了测试当layer放⼤时其⼦类的变化。layer进⾏缩放时,它的⼤⼩并没有发⽣改变,我们看到它变⼤或变⼩是因为它离我们的距离在发⽣改变,所以其⼦视图会随着⼀起变化。然⽽,改变其frame,它的⼤⼩和坐标是确实发⽣了改变,其⼦视图正常情况下是不会发⽣变化的。CABasicAnimation⽤法⾮常简单,下⾯介绍其⼦类CASpringAnimation。
⼆、CASpringAnimation
1.什么是CASpringAnimation
  CASpringAnimation是在CABasicAnimation的基础上衍⽣的另⼀个动画类,它⽐CABasicAnimation多了动画的弹性,是动画不再是从⼀个状态变成另⼀个状态时显得⽣硬。CASpringAnimation是iOS9.0之后新加的。
2.新增属性
  @property CGFloat mass;      //质量(影响弹簧的惯性,质量越⼤,弹簧惯性越⼤,运动的幅度越⼤)
  @property CGFloat stiffness;  //弹性系数(弹性系数越⼤,弹簧的运动越快)
  @property CGFloat damping;    //阻尼系数(阻尼系数越⼤,弹簧的停⽌越快)
  @property CGFloat initialVelocity;  //初始速率(弹簧动画的初始速度⼤⼩,弹簧运动的初始⽅向与初始速率的正负⼀致,若初始速率为0,表⽰忽略该属性)
  @property CGFloat settlingDuration; //结算时间(根据动画参数估算弹簧开始运动到停⽌的时间,动画设置的时间最好根据此时间来设置)
  这三个属性可以设置动画在执⾏到最终状态后的弹性效果,具体值需要调试,下⾯给出⼀个具体的类来实现这⼀功能。
1#import"SpringAnimationView.h"
2#import"UIView+HPAdditions.h"
3
4@interface SpringAnimationView()<UITableViewDelegate,UITableViewDataSource>
5 @property (nonatomic, strong) UIView *squareView;
6 @property (nonatomic, strong) UITableView *tableView;
7 @property (nonatomic, strong) NSMutableArray *dataSource;
8 @property (nonatomic, strong) UISlider *massSlider;
9 @property (nonatomic, strong) UISlider *stiffnessSlider;
10 @property (nonatomic, strong) UISlider *dampingSlider;
11 @property (nonatomic, strong) UILabel *massLabel;
12 @property (nonatomic, strong) UILabel *stiffnessLabel;
13 @property (nonatomic, strong) UILabel *dampingLabel;
14@end
15
16@implementation SpringAnimationView
17
18 - (instancetype)init{
19if (self = [super initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]) {
20        [self initData];
21        [self setupUI];
22    }
23return self;
24 }
25 - (void)setupUI{
26    self.squareView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 80)];
27    self.squareView.backgroundColor = [UIColor cyanColor];
28    self.squareView.layer.borderColor = [UIColor redColor].CGColor;
29    = CGPointMake(SCREEN_WIDTH/2.0, 200);
30    self.squareView.layer.shadowOpacity = 0.6;
31    self.squareView.layer.shadowOffset = CGSizeMake(0, 0);
32    self.squareView.layer.shadowRadius = 4;
33    self.squareView.layer.shadowColor = [UIColor redColor].CGColor;
34    [self addSubview:self.squareView];
35
36    self.massSlider = [[UISlider alloc] initWithFrame:CGRectMake(20, 0, 100, 40)];
37    self.massSlider.minimumValue = 0;
38    self.massSlider.maximumValue = 1;
39    self.massSlider.tintColor = [UIColor cyanColor];
40    self.massSlider.thumbTintColor = [UIColor redColor];
41    p = self.squareView.bottom + 100;
42    [self addSubview:self.massSlider];
43    [self.massSlider addTarget:self action:@selector(durationChange:) forControlEvents:UIControlEventValueChanged];
44
45    self.stiffnessSlider = [[UISlider alloc] initWithFrame:CGRectMake(20, 0, 100, 40)];
46    self.stiffnessSlider.minimumValue = 0;
47    self.stiffnessSlider.maximumValue = 100;
48    self.stiffnessSlider.tintColor = [UIColor cyanColor];
49    self.stiffnessSlider.thumbTintColor = [UIColor redColor];
50    p = self.massSlider.bottom + 10;
51    [self addSubview:self.stiffnessSlider];
52    [self.stiffnessSlider addTarget:self action:@selector(durationChange:) forControlEvents:UIControlEventValueChanged];
53
54    self.dampingSlider = [[UISlider alloc] initWithFrame:CGRectMake(20, 0, 100, 40)];
55    self.dampingSlider.minimumValue = 0;
56    self.dampingSlider.maximumValue = 10;
57    self.dampingSlider.tintColor = [UIColor cyanColor];
58    self.dampingSlider.thumbTintColor = [UIColor redColor];
59    p = self.stiffnessSlider.bottom + 10;
60    [self addSubview:self.dampingSlider];
61    [self.dampingSlider addTarget:self action:@selector(durationChange:) forControlEvents:UIControlEventValueChanged];
62
63    self.massLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
64    = @"mass";
65    Y = Y;
66    self.massLabel.left = self.massSlider.right+10;
67    Alignment = NSTextAlignmentLeft;
68    Color = [UIColor blackColor];
69    self.massLabel.font = [UIFont systemFontOfSize:17];
70    [self addSubview:self.massLabel];
71
72    self.stiffnessLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
73    = @"stiffness";
74    Y = Y;
75    self.stiffnessLabel.left = self.stiffnessSlider.right+10;
76    Alignment = NSTextAlignmentLeft;
77    Color = [UIColor blackColor];
78    self.stiffnessLabel.font = [UIFont systemFontOfSize:17];
79    [self addSubview:self.stiffnessLabel];
80
81    self.dampingLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
82    = @"damping";
83    Y = Y;
84    self.dampingLabel.left = self.dampingSlider.right+10;
85    Alignment = NSTextAlignmentLeft;
86    Color = [UIColor blackColor];
87    self.dampingLabel.font = [UIFont systemFontOfSize:17];
88    [self addSubview:self.dampingLabel];
89
90    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, self.dampingSlider.bottom+20, SCREEN_WIDTH, SCREEN_HEIGHT-self.dampingSlider.bottom-20) style:UITableViewStylePlain];
91    self.tableView.delegate = self;
92    self.tableView.dataSource = self;
93    [self addSubview:self.tableView];
94 }
95
96/* The mass of the object attached to the end of the spring. Must be greater
97 than 0. Defaults to one. */
98
99//@property CGFloat mass;
100
101/* The spring stiffness coefficient. Must be greater than 0.
102 * Defaults to 100. */
103
104//@property CGFloat stiffness;
105
106/* The damping coefficient. Must be greater than or equal to 0.
107 * Defaults to 10. */
108
109//@property CGFloat damping;
110
111/* The initial velocity of the object attached to the spring. Defaults
112 * to zero, which represents an unmoving object. Negative values
113 * represent the object moving away from the spring attachment point,
114 * positive values represent the object moving towards the spring
115 * attachment point. */
116
117//@property CGFloat initialVelocity;
118
119 - (CASpringAnimation *)getAnimationKeyPath:(NSString *)keyPath fromValue:(id)fromValue toValue:(id)toValue{
120    CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:keyPath];
121    springAnimation.fromValue = fromValue;
122    Value = toValue;
123    springAnimation.mass = self.massSlider.value;
124    springAnimation.stiffness = self.stiffnessSlider.value;
125    springAnimation.damping = self.dampingSlider.value;
126    springAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];;
127    springAnimation.duration = 2;
128    peatCount = 1;
129/* animation remove from view after animation finish */
130    vedOnCompletion = YES;
131/*
132只有当removedOnCompletion设置为no时,fillmode设置为kCAFillModeBoth或者kCAFillModeForwards才有效,
133    kCAFillModeRemoved //动画执⾏完成后回到初始状态
134    kCAFillModeBackwards //动画执⾏完成后回到初始状态
135    kCAFillModeForwards //动画执⾏完成后保留最后状态
136    kCAFillModeBoth //动画执⾏完成后保留最后状态
137*/
138    springAnimation.fillMode = kCAFillModeForwards;
139/*
140动画执⾏完成后按原动画返回执⾏,default no
141*/
142//    springAnimation.autoreverses = YES;
143return springAnimation;
144 }
145 - (void)initData{
146/*
147    kCAScaleZ 缩放z 没有意义,因为是平⾯图形
148    kCAPositionX设置y没有意义,可以随意设置,同理kCAPositionY设置x没有意义
149    kCABackgroundColor,颜⾊变化必须要⽤CGColor
150⽤到shadow的⼏个属性变化的时候,需要先设置shadow
151*/
152    NSValue *shadowStartPoint = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
153    NSValue *shadowEndPoint = [NSValue valueWithCGPoint:CGPointMake(5, 5)];
154id startColor = (id)([UIColor cyanColor].CGColor);
155id endColor = (id)([UIColor redColor].CGColor);
156id shadowStartColor = (id)[UIColor clearColor].CGColor;
157id shadowEndColor = (id)[UIColor redColor].CGColor;
158    self.dataSource = [NSMutableArray array];
159    NSArray *keypaths  = @[kCARotation,kCARotationX,kCARotationY,kCARotationZ,
160                            kCAScale,kCAScaleX,kCAScaleZ,kCAPositionY,
161                            kCABoundsSizeW,kCAOpacity,kCABackgroundColor,kCACornerRadius,
162                            kCABorderWidth,kCAShadowColor,kCAShadowRadius,kCAShadowOffset];
163
164    NSArray *fromValues = @[@0,@0,@0,@0,
165                            @0,@0,@0,@0,
166                            @100,@1,startColor,@0,
167                            @0,shadowStartColor,@0,shadowStartPoint];
168
169    NSArray *toValues  = @[@(M_PI),@(M_PI),@(M_PI),@(M_PI),
170                            @1,@1,@1,@400,
171                            @200,@0,endColor,@40,
172                            @4,shadowEndColor,@8,shadowEndPoint];
173for (int i=0; i&unt; i++) {
174        AnimationModel *model = [[AnimationModel alloc] init];
175        model.keyPaths = keypaths[i];
176        model.fromValue = fromValues[i];
177        Value = toValues[i];
178        [self.dataSource addObject:model];
179    }
180 }
181 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
182return1;
183 }
184 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
185return [self.dataSource count];
186 }
187 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
188    AnimationModel *model = [self.dataSource w];
189    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellID"];
190if (cell==nil) {
191        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellID"];
192    }
193    = model.keyPaths;
194    cell.selectionStyle = 0;
195return cell;
196 }
197 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
198    [self.squareView.layer removeAllAnimations];
199    AnimationModel *model = [self.dataSource w];
200    CABasicAnimation *animation = [self getAnimationKeyPath:model.keyPaths fromValue:model.fromValue Value]; 201    [self.squareView.layer addAnimation:animation forKey:@"animation"];
202 }
203 - (void)durationChange:(UISlider *)slider{
204    [slider setValue:slider.value];
205if (slider==self.massSlider) {
206        = [NSString stringWithFormat:@"mass  %.2f", slider.value];
207    }else if (slider==self.stiffnessSlider) {
208        = [NSString stringWithFormat:@"stiffness  %.2f", slider.value];
209    }else if (slider==self.dampingSlider) {
210        = [NSString stringWithFormat:@"damping  %.2f", slider.value];
211    }
212 }
213@end
214
215 --------------------------------------------------------------------------------------
216#import <UIKit/UIKit.h>
217
218@interface UIView (HPAdditions)
219 @property (nonatomic) CGFloat top;
220 @property (nonatomic) CGFloat bottom;
221 @property (nonatomic) CGFloat left;
222 @property (nonatomic) CGFloat right;
223
224 @property (nonatomic) CGFloat width;
225 @property (nonatomic) CGFloat height;
226 @property (nonatomic) CGPoint origin;
227 @property (nonatomic) CGSize size;
228
229 @property (nonatomic) CGFloat centerX;
230 @property (nonatomic) CGFloat centerY;
231
232@end
233
234#import"UIView+HPAdditions.h"
235
236@implementation UIView (HPAdditions)
237 - (CGFloat)top{
238return igin.y;
239 }
240 - (void)setTop:(CGFloat)top{
241    CGRect frame = self.frame;
242    igin.y = top;
243    self.frame = frame;
244 }
245 - (CGFloat)bottom{
246return igin.y+self.frame.size.height;
247 }
248 - (void)setBottom:(CGFloat)bottom{
249    CGRect frame = self.frame;
250    igin.y = bottom-frame.size.height;
251    self.frame = frame;
252 }
253 - (CGFloat)left{
254return igin.x;
255 }
256 - (void)setLeft:(CGFloat)left{
257    CGRect frame = self.frame;
258    igin.x = left;
259    self.frame = frame;
260 }
261 - (CGFloat)right{
262return igin.x+self.frame.size.width;
263 }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。