iOS开发之核心动画(二):常用动画类及方法介绍

不辞鶗鴂妒年芳,但惜流尘暗烛房。
昨夜西池凉露满,桂花吹断月中香。

李商隐 《昨夜》

CABasicAnimation

  • CABasicAnimation是核心动画类簇中的一个类,是CAPropertyAnimation的子类,它的祖父是CAAnimation。它主要用于制作比较单一的动画,例如,平移、缩放、旋转、颜色渐变、边框的值的变化等,也就是将layer的某个属性值从一个值到另一个值的变化。
  • 属性说明:
    • fromValue: keyPath相应属性的初始值
    • toValue: keyPath相应属性的结束值
  • 随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue。
  • 如果fillMode=kCAFillModeForwardsremovedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。
  • 但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。
  • 比如,CALayer的position初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为(100,100),虽然动画执行完毕后图层保持在(100,100)这个位置,实质上图层的position还是为(0,0)。

keyPath(官方文档截图)

hack_appList_result_failed

3D的形变属性

  • 在文档的CATransform3D key paths 这个位置查看keyPath的属性和包装的数值。
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
/** X Y 轴的动画
*/
- (void)demoTransform
{
// 创建基本动画
CABasicAnimation *anim = [CABasicAnimation animation];
// keyPath决定了执行怎样的动画(什么属性的动画)
anim.keyPath = @"transform.translation.y";

// toValue : 达到到的数值
// byValue : 增加某一个数值
anim.toValue = @(250);
// anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 2, 0)];

// 动画的执行时间
anim.duration = 2.0;

#pragma mark - 让图层保持在动画执行完的状态 一定要加这两句
// 动画执行完不要移除
anim.removedOnCompletion = NO;
// 保持在目前的状态位置
anim.fillMode = kCAFillModeForwards;

[self.layer addAnimation:anim forKey:nil]; // forKey是用来标记动画 移除的时候用
}

位置属性的基本动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** 位置属性的基本动画
*/
- (void)demoPosition
{
// 创建基本动画
CABasicAnimation *anim = [CABasicAnimation animation];
// keyPath决定了执行怎样的动画(什么属性的动画)
anim.keyPath = @"position.x";

// toValue : 达到到的数值
// byValue : 增加某一个数值
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(250, 250)];
anim.byValue = [NSValue valueWithCGPoint:CGPointMake(250, 250)];

#pragma mark - 让图层保持在动画执行完的状态 一定要加这两句
// 动画执行完不要移除
anim.removedOnCompletion = NO;
// 保持在目前的状态位置
anim.fillMode = kCAFillModeForwards;

[self.layer addAnimation:anim forKey:nil]; // forKey是用来标记动画 移除的时候用

}

旋转属性的基本动画

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
/** 旋转属性的基本动画
*/
- (void)demoRotation
{
// 创建基本动画
CABasicAnimation *anim = [CABasicAnimation animation];
// keyPath决定了执行怎样的动画(什么属性的动画)
anim.keyPath = @"transform.rotation";

// toValue : 达到到的数值
// byValue : 增加某一个数值
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_2_PI, 1, -1, 0)];
// anim.byValue = [NSValue valueWithCGPoint:CGPointMake(250, 250)];

// 动画的执行时间
anim.duration = 2.0;

#pragma mark - 让图层保持在动画执行完的状态 一定要加这两句
// 动画执行完不要移除
anim.removedOnCompletion = NO;
// 保持在目前的状态位置
anim.fillMode = kCAFillModeForwards;

[self.layer addAnimation:anim forKey:nil]; // forKey是用来标记动画 移除的时候用
}

缩放属性的基本动画

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
/** 缩放属性的基本动画
*/
- (void)demoScale
{
// 创建基本动画
CABasicAnimation *anim = [CABasicAnimation animation];
// keyPath决定了执行怎样的动画(什么属性的动画)
anim.keyPath = @"transform.scale";
// anim.keyPath = @"transform";
// anim.keyPath = @"bounds";
//
// toValue : 达到到的数值
// byValue : 增加某一个数值
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 2, 0)];
// anim.byValue = [NSValue valueWithCGPoint:CGPointMake(250, 250)];

// 动画的执行时间
anim.duration = 2.0;

#pragma mark - 让图层保持在动画执行完的状态 一定要加这两句
// 动画执行完不要移除
anim.removedOnCompletion = NO;
// 保持在目前的状态位置
anim.fillMode = kCAFillModeForwards;

[self.layer addAnimation:anim forKey:nil]; // forKey是用来标记动画 移除的时候用
}

keyFrameAnimation (关键帧动画)

  • keyFrameAnimation是CApropertyAnimation的子类,跟CABasicAnimation的区别是:
    • CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue)
    • 而CAKeyframeAnimation会使用一个NSArray保存这些数值
  • 属性说明:
    • values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
    • path:可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略。
    • keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0。keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的。
    • CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation。

帧动画的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)demo
{
// 创建帧动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
anim.keyPath = @"position";

// 可以添加多个值执行动画
NSValue *value1 = [NSValue valueWithCGPoint:CGPointZero];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(50, 50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(100, 150)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(250, 50)];
anim.values = @[value1,value2,value3,value4];

// 持续时间两秒
anim.duration = 2.0;

// 动画过后保持现在的状态
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;

// 添加动画
[self.purpleView.layer addAnimation:anim forKey:nil];

}

animation.path方法

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
/** 让view沿着绘制的路径执行动画
*/
- (void)demoCircle
{
// 创建动画
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
anim.keyPath = @"position";
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.duration = 2.0;

// 绘制圆路径
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(100, 100, 250, 250));
anim.path = path;
CGPathRelease(path); // 有创建就要手动 release

anim.delegate = self; // 代理就是view 不需要遵守协议

// 动画方程(动画的执行方式)
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

// 添加动画
[self.purpleView.layer addAnimation:anim forKey:nil];
}

#pragma mark - Animation代理方法
/** 动画开始调用的方法
*/
- (void)animationDidStart:(CAAnimation *)anim
{
NSLog(@"%s",__func__);
}
/** 动画执行结束调用的方法
*/
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
NSLog(@"%s",__func__);
}

图标抖动(模仿App删除)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (IBAction)star 
{
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
anim.keyPath = @"transform.rotation";

// 设置摆动角度
anim.values = @[@(Angle2Radian(-7)),@(Angle2Radian(7)),@(Angle2Radian(-7))];

anim.duration = 0.15;
// 执行次数
anim.repeatCount = CGFLOAT_MAX;
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;

[self.iconView.layer addAnimation:anim forKey:@"shake"];

}
- (IBAction)stop
{
[self.iconView.layer removeAnimationForKey:@"shake"];
}

CATransition

  • CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。
  • UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果。
  • 属性解析:
    • type:动画过渡类型
    • subtype:动画过渡方向
    • startProgress:动画起点(在整体动画的百分比)
    • endProgress:动画终点(在整体动画的百分比)

转场动画过渡效果

hack_appList_result_failed

CAAnimationGroup

  • CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。
  • 属性解析:
    • animations:用来保存一组动画对象的NSArray。
    • 默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 将多个核心动画放在动画组里面一起执行
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 动画1
CABasicAnimation *animRotate = [CABasicAnimation animation];
animRotate.keyPath = @"transform.rotation";
animRotate.toValue = @(M_2_PI);
// 动画2
CABasicAnimation *animScale= [CABasicAnimation animation];
animScale.keyPath = @"transform.scale";
animScale.toValue = @(0.5);
// 动画3
CABasicAnimation *animTranslate = [CABasicAnimation animation];
animTranslate.keyPath = @"transform.translation";
animTranslate.toValue = @(100);
// 放在动画组里面
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = @[animRotate,animScale,animTranslate];
animGroup.removedOnCompletion = NO;
animGroup.fillMode = kCAFillModeForwards;
animGroup.duration = 3.0;
[self.cyanView.layer addAnimation:animGroup forKey:nil];
}

UIView动画封装

  • UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持。
  • 执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视图,为此需要将改变属性的代码放在[UIView beginAnimations:nil context:nil][UIView commitAnimations]之间。

  • 常见方法解析:

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
// 设置动画代理对象,当动画开始或者结束时会发消息给代理对象
+ (void)setAnimationDelegate:(id)delegate

// 当动画即将开始时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
+ (void)setAnimationWillStartSelector:(SEL)selector

// 当动画结束时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
+ (void)setAnimationDidStopSelector:(SEL)selector

// 动画的持续时间,秒为单位
+ (void)setAnimationDuration:(NSTimeInterval)duration

// 动画延迟delay秒后再开始
+ (void)setAnimationDelay:(NSTimeInterval)delay

// 动画的开始时间,默认为now
+ (void)setAnimationStartDate:(NSDate *)startDate

// 动画的节奏控制,具体看下面的”备注”
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve

// 动画的重复次数
+ (void)setAnimationRepeatCount:(float)repeatCount

// 如果设置为YES,代表动画每次重复执行的效果会跟上一次相反
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses

// 设置视图view的过渡效果, transition指定过渡类型, cache设置YES代表使用视图缓存,性能较好
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache

Block动画

动画1

  • duration:动画的持续时间
  • delay:动画延迟delay秒后开始
  • options:动画的节奏控制
  • animations:将改变视图属性的代码放在这个block中
  • completion:动画结束后,会自动调用这个block
1
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

动画2

  • duration:动画的持续时间
  • view:需要进行转场动画的视图
  • options:转场动画的类型
  • animations:将改变视图属性的代码放在这个block中
  • completion:动画结束后,会自动调用这个block
1
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

动画3

  • duration:动画的持续时间
  • options:转场动画的类型
  • animations:将改变视图属性的代码放在这个block中
  • completion:动画结束后,会自动调用这个block
1
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
  • 方法调用完毕后,相当于执行了下面两句代码:
1
2
3
4
// 添加toView到父视图
[fromView.superview addSubview:toView];
// 把fromView从父视图中移除
[fromView.superview removeFromSuperview];

UIImageView的帧动画

  • UIImageView可以让一系列的图片在特定的时间内按顺序显示。
  • 相关属性解析:
    • animationImages:要显示的图片(一个装着UIImage的NSArray)。
    • animationDuration:完整地显示一次animationImages中的所有图片所需的时间。
    • animationRepeatCount:动画的执行次数(默认为0,代表无限循环)。
  • 相关方法解析:
1
2
3
- (void)startAnimating; 开始动画
- (void)stopAnimating; 停止动画
- (BOOL)isAnimating; 是否正在运行动画

UIActivityIndicatorView

  • 是一个旋转进度轮,可以用来告知用户有一个操作正在进行中,一般用initWithActivityIndicatorStyle初始化。
  • UIActivityIndicatorViewStyle有3个值可供选择:

    • UIActivityIndicatorViewStyleWhiteLarge:大型白色指示器
    • UIActivityIndicatorViewStyleWhite:标准尺寸白色指示器
    • UIActivityIndicatorViewStyleGray:灰色指示器,用于白色背景
  • 方法解析:

1
2
3
- (void)startAnimating; 开始动画
- (void)stopAnimating; 停止动画
- (BOOL)isAnimating; 是否正在运行动画
要不要鼓励一下😘