CoreAnimation,译为核心动画。它是一组非常抢到的动画处理框架(供Mac OS和iOS平台使用),使用它可以做出非常绚丽的动画效果,而且使用起来非常简单。
CoreAnimation的动画执行是在后台操作的,不会阻塞主线程。动画是直接作用在CALayer
上的,不是UIView
。
一、核心动画的基础
1.1. 继承结构
CAAnimation
是所有动画的父类,CAAnimation
遵守了CAMediaTiming
协议。
1.2. 创建动画
CAAnimation
是作用在Layer上的,所以说先需要创建CALayer
,然后初始化一个CAAnimation
对象,并设置一些动画属性,最后通过调用CALayer
的addAnimation:forKey:
方法,把CAAnimation
对象添加到CALayer
中,这样就能看到动画了。
addAnimation:forKey:
方法中key的作用是用来区分动画的唯一标识,如果只有一个动画,可以设为nil。
示例:对红色view添加核心动画。
1 | - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { |
手指点击一次,动画执行一次。而且动画直接结束后立马回到原来的位置。下面介绍的两个属性可以调整状态:
CAAnimation
属性removedOnCompletion
可以控制动画完成后是否移除动画,默认是YES。CAMediaTiming
协议属性fillMode
可以设置动画完成时layer的状态。backwards(kCAFillModeBackwards)
:原来位置forwards(kCAFillModeForwards)
:动画目标位置both(kCAFillModeBoth)
:随机选择backwards
或forwards
removed(kCAFillModeRemoved)
:移除动画(默认)
1.3. 心跳效果(CABasicAnimation)
心跳效果主要是比例缩放。
repeatCount
:重复次数,默认0duration
:动画执行时长autoreverses
:是否自动反转(从哪来到哪去的往返动画,默认NO)
1 | CABasicAnimation *anim = [CABasicAnimation animation]; |
1.4. 抖动效果(CAKeyframeAnimation)
抖动效果在苹果手机上经常看到,长按应用图标就会出现左右抖动。抖动的原理就是添加从左边弧度和右边弧度的往返帧动画。
values
:动画属性值,可以设置多个,多个值会均分duration
1 | CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; |
1.5. 路径动画(CAKeyframeAnimation和UIBezierPath)
路径动画只能使用CAKeyframeAnimation
来构建,路径使用UIBezierPath
创建。
CAKeyframeAnimation
有两个属性非常重要:
动画旋转模式
1 | @property(nullable, copy) CAAnimationRotationMode rotationMode; |
默认值为空,有两个值可供选择:
kCAAnimationRotateAuto
: 自动旋转kCAAnimationRotateAutoReverse
: 自动反转
时间计算模式
1 | @property(copy) CAAnimationCalculationMode calculationMode; |
默认值linear
,系统提供了字符串常量:
kCAAnimationLinear
: 匀时(每个路径动画时长均分总时长)kCAAnimationDiscrete
: 离散动画(只有关键帧有动画)kCAAnimationPaced
: 匀速(每个路径动画速度一致)kCAAnimationCubic
: 关键帧之间使用圆弧曲线动画kCAAnimationCubicPaced
: 在kCAAnimationCubic
基础上kCAAnimationPaced
动画
当设置计算模式为kCAAnimationPaced
和kCAAnimationCubicPaced
时,keyTimes
和timingFunctions
会失效。
1 | // 1.创建路径 |
1.6. 转场动画(CATransition)
转场效果
控制转场动画的过度效果
1 | @property(copy) CATransitionType type; |
suckEffect
收缩效果是从父视图左上角被收走。
转场方向
控制动画从哪个方向开始。
1 | @property(nullable, copy) CATransitionSubtype subtype; |
有四个常量值:
kCATransitionFromRight
:kCATransitionFromLeft
:kCATransitionFromTop
:kCATransitionFromBottom
:
动画起止点
1 | @property float startProgress; |
设置动画从哪个点开始,到哪个点结束。数值范围是[0, 1]
,endProgress
必须大于等于startProgress
。他们的默认值分别是0和1。
示例
1 | NSInteger _index = 0; |
注意:转场代码和转场动画必须在同一个方法中(编译后),否则动画无效。
1.7. 动画组(CAAnimationGroup)
动画组可以把多个动画合并在一起执行。
可以把需要组合在一起的动画放到animations
数组中,数组元素需是CAAnimation
类型,也就是所有的核心动画都可以放到数组中。
1 | @property(nullable, copy) NSArray<CAAnimation *> *animations; |
示例:
1 | CABasicAnimation *anim_scale = [CABasicAnimation animation]; |
上面示例代码有很多重复属性值,如果把动画添加到动画组就会省下很多代码:
1 | CABasicAnimation *anim_scale = [CABasicAnimation animation]; |
1.8. 代理(CAAnimationDelegate)
所有核心动画都有都可以成为代理。
1 | // 动画开始时调用 |
二、探究核心动画前后的位置和尺寸
核心动画并没有修改属性的真实值,那为什么看到的内容确实发生了形变呢?我们打印动画执行前后控件的位置看下。
场景:屏幕上红色view添加核心动画,通过形变查看前后位置。
1 | - (void)viewDidLoad { |
输出:
1 | origin:{{10, 60}, {100, 100}} |
唉,为什么呢?系统出bug了?哈哈哈哈
这是因为layer层其实是一个数据结构,含有模型层和动画层。真实数据都是在模型层,动画修改的是动画层的数据,并没有修改模型层,所以真实位置没有发生改变。
三、UIView动画和CALayer核心动画区别
区别:
- 核心动画只作用在
Layer
上; - 核心动画看到的一切都是假象,并没有去修改属性的真实值;
选择:
- 当需要与用户进行交互时,由于
CALayer
不能和用户交互并且位置是虚假的,所以必须使用UIView
动画; - 帧动画:当需要根据路径做动画时,使用核心动画;
- 转场动画:使用核心动画,转场类型比较多。
四、综合案例
4.1. 图片折叠
思路:
- 创建上下两个
UIImageView
,两个控件重叠在一起; - 图片利用代码来控制显示部分内容,上面的图片只显示上半部分,下面的图片显示下半部分(也可以用之前裁剪好的图片);
- 因为是按照x轴旋转,所以修改上面
UIImageView
的锚点为(0.5, 1.0)
; - 下面的
UIImageView
也需要修改锚点(0.5, 0.0)
,否则两个图片之间会有间隙; - 这时候视觉效果已经是一张完整的图片了,而且上面的图片也可以围绕x轴旋转;
- 由于整个
UIImageView
都需要手势控制,所以我们在图片上方覆盖一个透明的view,在这个view上添加手势来控制图片的形变。 - 为上面的图片添加透视效果;
- 为下面的图片添加阴影渐变。
知识点一:显示部分内容CALayer
有一个contentsRect
属性,可以控制内容显示区域,范围是[0 0 1 1]
,就是从左上角开始,整个layer层的区域大小是按照[0, 1]
计算的。设置范围后,会裁剪指定范围外的内容,只保留范围内的内容,并填充整个layer的范围。
1 | @property CGRect contentsRect; |
例,显示左半部分的范围:{0, 0, 0.5, 1}
知识点二:透视效果
核心动画本质是一个矩阵变换的过程,涉及到矩阵的计算。但是透视效果实现起来非常简单,我们只需要修改图层的初始矩阵第三行四列元素(m34)的值就行了。
1 | CATransform3D transform = CATransform3DIdentity; |
在视觉原理上,透视出来的效果是近大远小,距离镜头近的物体就大,越远越小。所以,我们只需要修改分母的值来确定镜头的位置(分母一定要是小数)。
知识点三:弹性动画
弹性动画用到地方很多,微博的选取相册界面就是用的弹性动画。
1 | /** |
代码:ViewController
1 | @interface ViewController ()<CAAnimationDelegate> |
4.2. 音乐振动条
思路:
- 创建一个背景视图,用来装载振动条,振动条使用
CALayer
绘制; - 震动的原理是给振动条添加y轴的形变,只要y轴不断的变大和变小就能达到震动的效果;
- 把振动条多复制几份并设置一定间距,整体效果就有了。
知识点:CAReplicatorLayer
是CALayer
的子类,见名知意,他可以用来复制Layer,复制的是添加到自己身上的所有子层。
设置复制的份数,包含已添加到身上的。
1 | @property NSInteger instanceCount; |
对每一份复制的内容整体做形变操作(不包括首次添加上去的)
1 | @property CATransform3D instanceTransform; |
设置每一份复制内容动画延迟执行的时间(每一份等待时间是n,不包括首次添加上去的)
1 | @property CFTimeInterval instanceDelay; |
设置每一份复制内容的RGBA
1 | @property float instanceRedOffset; |
代码:
1 | // 1.创建复制容器 |
4.3. 倒影
思路:
- 自定义View,重写layer类为
CAReplicatorLayer
,添加一个UIImageView
到view上; - 使用
CAReplicatorLayer
复制一份子层; - 旋转子层180°(注意锚点位置),并设置复制层的RGBA。
代码:
此案例是重写控制器的view。
1 | // 自定义控制器view |