1. 介绍
1.1 CoreAnimation
CoreAnimation是苹果提供的一套基于绘图的动画框架,下图是官方文档中给出的体系结构:
从图中可以看出,最底层是图形硬件(GPU);上层是OpenGL和CoreGraphics,提供一些接口来访问GPU;再上层的CoreAnimation在此基础上封装了一套动画的API。最上面的UIKit属于应用层,处理与用户的交互。所以,学习CoreAnimation也会涉及一些图形学的知识,了解这些有助于我们更顺手的使用以及更高效的解决问题。
1.2 CALayer
为什么UIView要加一层Layer来负责显示呢?我们知道QuartzCore是跨iOS和macOS平台的,而UIView属于UIKit是iOS开发使用的,在macOS中对应AppKit里的NSView。这是因为macOS是基于鼠标指针操作的系统,与iOS的多点触控有本质的区别。虽然iOS在交互上与macOS有所不同,但在显示层面却可以使用同一套技术。
每一个UIView都有个属性layer、默认为CALayer类型,也可以使用自定义的Layer:
/* view的leyer,view是layer的代理 */
@property(nonatomic,readonly,strong) CALayer *layer;
可以想象我们看到的View其实都是它的layer,下面我们通过CALayer中的集合相关的属性来认识它:
bounds:图层的bounds是一个CGRect的值,指定图层的大小(bounds.size)和原点(bounds.origin)
position:指定图层的位置(相对于父图层而言)
anchorPoint:锚点指定了position在当前图层中的位置,坐标范围0~1。position点的值是相对于父图层的,而这个position到底位于当前图层的什么地方,是由锚点决定的。(默认在图层的中心,即锚点为(0.5,0.5) )
transform:指定图层的几何变换,类型为上篇说过的CATransform3D
这些属性的注释最后都有一句Animatable,就是说我们可以通过改变这些属性来实现动画。默认地,我们修改这些属性都会导致图层从旧值动画显示为新值,称为隐式动画。
注意到frame的注释里面是没有Animatable的。事实上,我们可以理解为图层的frame并不是一个真实的属性:当我们读取frame时,会根据图层position、bounds、anchorPoint和transform的值计算出它的frame;而当我们设置frame时,图层会根据anchorPoint改变position和bounds,也就是说frame本身并没有被保存。
position和anchorPoint详解,这篇文章中有详细介绍。
1.3 CAAnimation
CALayer提供以下方法来管理动画:
- (void)addAnimation:(CAAnimation*)anim forKey:(nullable NSString*)key;
- (void)removeAllAnimations;
- (void)removeAnimationForKey:(NSString*)key;
- (nullable NSArray*)animationKeys;
- (nullable CAAnimation*)animationForKey:(NSString*)key;
CAAnimation是动画基类,我们常用的CABasicAnimation和CAKeyframeAnimation都继承于CAPropertyAnimation即属性动画。属性动画通过改变layer的可动画属性(位置、大小等)实现动画效果,下面主要是对CAAnimation做一个详细的记录。
2. CAAnimation
2.1 实现动画的方式
如果根据实现动画时直接操作对象的类型,我们可以简单的将动画分为视图和图层两种;但事实上,无论UIViewAnimaiton动画还是UIViewAnimaitonWithBlock动画都只是对UIView的关联图层CALayer动画的进一步封装。
2.2 CAAnimation相关类之间的关系
我们在使用Core Animation动画之前,有必要对核心动画常见的类和动画属性做一个基本了解;从继承关系的图示中,我们可以十分清晰的看出这些属性设置设置因何而来,以及它们各自的联系。
动画类 | 动画特性 |
---|---|
CAMediaTiming | 协议;定义了一段动画内用于控制时间的属性的集合 |
CAAnimation | 抽象类;作为所有动画类型父类,不可直接使用 |
CAPropertyAnimation | 抽象类;作为基础动画和帧动画的父类,不可直接使用 |
CABasicAnimation | 基础动画;用于实现单一属性变化的动画 |
CAKeyFrameAnimation | 关键帧动画;用于实现单一属性连续变化的动画 |
CAAnimaitionGroup | 组动画;用于实现多属性同时变化的动画 |
CATrasition | 转场过渡动画; |
2.3 CAMediaTiming协议
CAMediaTiming协议定义了一段动画内用于控制时间的属性的集合,CALayer和CAAnimation都实现了这个协议,所以时间可以被任意基于一个图层或者一段动画的类控制,有关CAMediaTimg协议具体的属性如下:
@protocol CAMediaTiming
// 动画开始之前的延迟时间,这里的延迟从动画添加到可见图层上那一刻开始测量;
// (设置动画beginTime为1,动画将延时1秒后开始执行)
@property CFTimeInterval beginTime;
// 动画持续时间;(默认值为0,但是实际动画默认持续时间为0.25秒)
@property CFTimeInterval duration;
// 动画执行的速度;
// (默认值为0,减少它会减慢动画的时间,增加它会加快速度)
// (设置speed为2时,则动画实际执行时间是duration的一半)
@property float speed;
// 动画时间偏移量;
// (设置时长3秒动画的timeOffset为1时,动画会从1秒位置执到最后,再执行之前跳过的部分)
@property CFTimeInterval timeOffset;
// 动画重复次数;默认值是0,但是实际默认动画执行1次;
// (设置为INFINITY,则一直执行);
// (duration是2,repeatCount设置为3.5,则完整动画时长7秒)
@property float repeatCount;
// 设置为INFINITY,一直执行
// repeatCount和repeatDuration可能会相互冲突,所以你只需要对其中一个指定非零值,对两个属性都设置非0值的行为没有被定义;
@property CFTimeInterval repeatDuration;
// 动画从初始值执行到最终值,是否会反向回到初始值;
// (设置为YES,动画完成后将以动画的形式回到初始位置)
@property BOOL autoreverses;
// 决定当前对象在非动画时间端段的动画属性值,如动画开始之前和动画结束之后
@property(copy) CAMediaTimingFillMode fillMode;
@end
2.3.1 fillMode详细说明
试想这样一个问题:在beginTime非0(即动画未真正执行之前),以及removeOnCompletion被设置为NO的动画结束时,我们会遇到这样一个问题:被设置动画的属性应该是什么值?
一种可能是属性与动画没被添加之前保持一致,还有一种可能是保持动画开始之前那一帧或者动画结束那一帧,这就是所谓的填充。
CAMediaTiming的fillMode用来控制填充效果,它是一个NSString类型,有四种常量可供使用:
fillMode类型 | 参数类型 | 具体描述 |
---|---|---|
kCAFillModeRemoved (default) | NSString | 默认值,动画开始前和结束后,动画对图层都没有影响,图层依然保持初始值 |
kCAFillModeForwards | NSString | 动画结束后,图层一直保持动画后的最终状态 |
kCAFillModeBackwards | NSString | 动画开始前,只要加入动画就会处于动画的初始状态 |
kCAFillModeBoth | NSString | 综合了kCAFillModeForwards与kCAFillModeBackwards特性;(动画加入图层到真正执行动画的时间段里,图层保持动画初始状态;动画结束之后保持动画最终状态) |
特别注意:removedOnCompletion需要设置为NO,否则fillMode不起作用;
2.3.2 CAMediaTiming属性应用总结
2.4 CAAnimation基类
CAAnimation作为所有动画类型父类,是一个抽象类;我们不能直接使用CAAnimation类,而是使用它的子类,CAAnimation
遵守CAMediaTiming
和CAAction
协议,它是直接作用在CALayer
上的, 并非UIView
上,动画执行过程不在主线程上进行,所以不会阻塞主线程。先来看看CAnimation
都定义了哪些属性,方法:
@interface CAAnimation : NSObject
// 初始化一个CAAnimation 对象
+ (instancetype)animation;
// 一般用在自定义的动画中,与CALayer的同名方法一样。例如我们在自定义的动画的实现文件中覆写父类的这个方法。
// 那么,我们在用到这个自定义动画的时候,在不设置时间duration的情况下,都是2s。
+ (nullable id)defaultValueForKey:(NSString *)key;
// 子类重写这个方法设置属性是否可以被归档
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
// 一个动画与时间的函数。默认的函数是一条直线,也就是动画均匀变化
@property(nullable, strong) CAMediaTimingFunction *timingFunction;
// 动画代理
@property(nullable, strong) id delegate;
// 当这个值是YES的时候,当前动画对象会在动画时间duration结束的时候会从render tree树上移除掉。默认是YES
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
@end
2.4.1 动画缓冲属性timingFunction
动画的过渡方式,尤其是加速和减速的过程。timingFunction属性是CAMediaTimingFunction类的一个对象,用来控制图层动画变换的速度;使用它需要调用+functionWithName:的构造方法,下面是可传入的变量的介绍:
变量名 | 具体说明 |
---|---|
KCAMediaTimingFuncationLinear | 默认,匀速执行动画 |
KCAMediaTimingFuncationEaseIn | 先慢慢加速,后突然停止 |
KCAMediaTimingFuncationEaseOut | 先全速开始,再慢慢减速停止 |
KCAMediaTimingFuncationEaseInEaseOut | 先慢慢加速,再慢慢减速 |
KCAMediaTimingFuncationDefault | 效果同KCAMediaTimingFuncationEaseInEaseOut |
通过这种方法控制动画速度,其实是使用不同的变量创建了不同的计时函数。比如KCAMediaTimingFuncationLinear选项创建的是一个线性的计时函数,这也是CAAnimation的timingFunction属性为空时候的默认函数。
2.4.2 动画代理属性delegate
/* Delegate methods for CAAnimation. */
@protocol CAAnimationDelegate
@optional
//动画开始时调用
- (void)animationDidStart:(CAAnimation *)anim;
//动画结束时调用
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
@end
2.4.3 removedOnCompletion
removedOnCompletion属性默认为YES,表示动画完成后就会从图层上移除,图层也会恢复到动画执行前的状态;当其修改为NO时,那么图层将会保持动画结束后的状态,此时的fillMode属性也将生效;
另外,removedOnCompletion设置为NO时,直到我们手动移除动画,否则动画将不会自动释放;所以通常我们此时会给动画添加一个非空的键,这样可以在不需要动画的时候把它从图层上移除;
这里提一下,在动画执行的过程中,应用回到了后台,这个动画会被立马停止,然后调用delegate的
animationDidStop:finished:
方法
如果想要保持回到前台动画还在执行,就设置为NO。
2.5 CAPropertyAnimation基类
CAPropertyAnimation是一个抽象类,不能直接用于实现CALayer动画操作,相关属性和方法如下:
@interface CAPropertyAnimation : CAAnimation
// 创建动画实例的同时定义动画的方式
+ (instancetype)animationWithKeyPath:(nullable NSString *)path;
// 用来描述动画的方式,如“position”“lineWidth”,
// 描述中只要包含`Animatable`的属性就都可以用来做动画。
@property(nullable, copy) NSString *keyPath;
// 默认是NO,当设置为YES的时候,使 Core Animation 在更新 presentation layer 之前将动画的值添加到 model layer 中去。
// 这个属性确定动画执行的状态是否叠加在控件的原状态上,
// 默认设置为NO,如果我们执行两次位置移动的动画,会从同一位置执行两次
// 如果设置为YES,则会在第一次执行的基础上执行第二次动画
@property(getter=isAdditive) BOOL additive;
// `cumulative`一般和`repeatCount`(动画重复的次数)结合使用,当`cumulative`被设为YES时,下次动画的值会在上一次动画值的基础上进行而不是重新回到初始状态
@property(getter=isCumulative) BOOL cumulative;
@property(nullable, strong) CAValueFunction *valueFunction;
@end
2.5.1 相关keyPath
只能修改一个keyPath,总结这些属性如下:
属性 | 解读 |
---|---|
transform.rotation | 默认围绕z轴旋转,相当于transform.rotation.z |
transform.rotation.x transform.rotation.y transform.rotation.z |
分别围绕x轴、y轴、z轴旋转; |
transform.scale | 在所有方向上进行缩放 |
transform.scale.x transform.scale.y transform.scale.z |
分别在x轴、y轴、z轴方向上缩放; |
transform.translation | 平移到指定坐标点 |
transform.translation.x transform.translation.y transform.translation.z |
分别在x轴、y轴、z轴方向上平移; |
zPosition | z轴位置 |
opacity | 透明度 |
backgroundColor | 背景颜色 |
cornerRadius | 圆角大小 |
borderWidth | 边框宽度 |
bounds | 图层大小 |
contents | 寄宿图内容 |
contentsRect | 可视内容 |
position | 图层位置,类似transform.translation |
shadowColor | 阴影颜色 |
shadowOffset | 阴影偏移 |
shadowOpacity | 阴影透明度 |
shadowRadius | 阴影角度 |
附:KeyPath官方参考链接
2.6 CABasicAnimation基础动画
2.6.1 相关属性和方法
CABasicAnimation即基础动画,在指定可动画属性后,动画会按照预定的参数持续一定时间由初始值变换为终点值。其实,CABasicAnimation就相当于只有开始和结束两个帧的特殊关键帧动画(后续会详解);
@interface CABasicAnimation : CAPropertyAnimation
// 起始值
@property(nullable, strong) id fromValue;
// 结束值
@property(nullable, strong) id toValue;
// keyPath属性的变化值
@property(nullable, strong) id byValue;
@end
2.6.2 举例说明
下面示例使用CABasicAnimation实现了一个视图围绕中心点旋转的动画:
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation.z";
animation.fromValue = @(0);
animation.toValue = @(M_PI);
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = NO;
animation.duration = 1.5;
animation.repeatCount = INFINITY;
animation.delegate = self;
[_childView.layer addAnimation:animation forKey:@"animation"];
2.7 关键帧动画CAKeyframeAnimation
CACAKeyfameAnimation是CAPropertyAnimation的另一个子类,它和和CABasicAnimation一样都只能作用于图层对象的单一属性;它们的区别在于:CACAKeyfameAnimation不限制于设置一个起始值和结束值,而是可以根据一连串的值来做动画。其实,CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation。
2.7.1 相关属性和方法
@interface CAKeyframeAnimation : CAPropertyAnimation
// 用于提供关键帧数据的数组,数组中每一个值都对应一个关键帧属性值;
// 数组中的数据类型根据动画类型(KeyPath)而不同;
// 当使用path的时候,values的值将会被自动忽略;
@property(nullable, copy) NSArray *values;
// 用于提供关键帧数据的路径;
// path与values属性作用相同,但是两者互斥,同时指定values和
// path,path会覆盖values的效果;
// 一般不直接使用它,使用的是`UIBezierPath`,`UIBezierPath`是`CGPathRef`数据类型的封装。
@property(nullable) CGPathRef path;
// ktyTimes与Values中的值具有一一对应的关系,用于指定关键帧在动画的时间点,取值范围是[0,1];
// 若没有设置keyTimes,则每个关键帧的时间是平分动画总时长(duration);
@property(nullable, copy) NSArray *keyTimes;
// 用于指定每个关键帧之间的动画缓冲效果,这类似于物体运动的加速度;
// 注意:存在几个子路径就应该在此数组中传入几个元素;
@property(nullable, copy) NSArray *timingFunctions;
// 该属性决定了物体在每个子路径下是跳着走还是匀速走,跟timeFunctions属性有点类似;
@property(copy) CAAnimationCalculationMode calculationMode;
// 这三个属性是对每一帧的细节的设置,分别是张力值,持续性值,偏移值。
@property(nullable, copy) NSArray *tensionValues;
@property(nullable, copy) NSArray *continuityValues;
@property(nullable, copy) NSArray *biasValues;
// 设置帧动画是否需要按照路径切线的方向运动;path有值得时候才有用。
// 这两个都可以让做动画的时候沿着路径的切线方向,但是也有稍微的不同,看下面的演示就清楚了。
@property(nullable, copy) CAAnimationRotationMode rotationMode;
@end
2.7.2 CAAnimationRotationMode 说明
只有在设置path的情况下有作用,只有两个值,相关值和效果如下:
-
kCAAnimationRotateAuto
-
kCAAnimationRotateAutoReverse
2.7.3 举例说明
- 设置values数组
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.values = @[@(CGPointMake(125, 125)),
@(CGPointMake(325, 125)),
@(CGPointMake(325, 325)),
@(CGPointMake(125, 325)),
@(CGPointMake(125, 125))
];
animation.duration = 6;
animation.delegate = self;
[_childView.layer addAnimation:animation forKey:@"animation"];
- 设置path
这里使用贝塞尔曲线
// 执行动画的view
UIImageView *childView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 50, 50)];
childView.image = [UIImage imageNamed:@"qq"];
[self.view addSubview:childView];
// 贝塞尔
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 100, 300, 300) cornerRadius:30];
// 画个线
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
layer.strokeColor = UIColor.blackColor.CGColor;
layer.fillColor = UIColor.clearColor.CGColor;
[self.view.layer addSublayer:layer];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
// 设置路径
animation.path = bezierPath.CGPath;
animation.duration = 15;
animation.delegate = self;
[childView.layer addAnimation:animation forKey:@"animation"];
2.8 动画组CAGroupAnimation
2.8.1 介绍
动画组就是可以将不同的动画效果组合起来,CABasicAnimation和CAKeyframeAnimation都只能作用于单一的属性,而CAAnimationGrop可以设置其animations数组的属性来组合别的动画,从而达到混合多种动画效果的目的,简单明了。
@interface CAAnimationGroup : CAAnimation
@property(nullable, copy) NSArray *animations;
@end
2.8.2 举例说明
组合基础动画和关键帧动画,实现一个滑块,在按照path运动过程中修改其颜色:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 执行动画的view
UIView *childView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 50, 50)];
childView.backgroundColor = UIColor.redColor;
[self.view addSubview:childView];
// 贝塞尔
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 100, 300, 300) cornerRadius:30];
// 画个线
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
layer.strokeColor = UIColor.blackColor.CGColor;
layer.fillColor = UIColor.clearColor.CGColor;
[self.view.layer addSublayer:layer];
// 关键帧动画
CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animation];
keyAnimation.keyPath = @"position";
keyAnimation.path = bezierPath.CGPath;
keyAnimation.duration = 10;
keyAnimation.rotationMode = kCAAnimationRotateAuto;
// 基础动画
CABasicAnimation *basicAnimation = [CABasicAnimation animation];
basicAnimation.keyPath = @"backgroundColor";
basicAnimation.fromValue = (__bridge id _Nullable)(UIColor.redColor.CGColor);
basicAnimation.toValue = (__bridge id _Nullable)UIColor.blueColor.CGColor;
basicAnimation.duration = 3;
basicAnimation.fillMode = kCAFillModeForwards;
// 动画组
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[keyAnimation,basicAnimation];
animationGroup.duration = 10;
[childView.layer addAnimation:animationGroup forKey:@"animationGroup"];
}
2.9 过渡动画CATransition
2.9.1 简介
属性动画只能对图层的可动画属性起作用,而过渡动画可以改变非动画属性(比如交换一段文本和图片),或者从层级关系中添加or移除图层,于是就有了过渡的概念。
过渡动画使用CATransition来实现,它同样是CAAnimation的子类;它并不像属性动画那样在平滑的两个值之间做动画,而是影响到整个图层的变化。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。
过渡动画通常用于删除子控件、添加子控件、切换两个子控件等。
@interface CATransition : CAAnimation
// 用于控制整体动画效果类型
@property(copy) CATransitionType type;
// 用于控制动画方向
@property(nullable, copy) CATransitionSubtype subtype;
// 到开始和结束执行的转换的进度量。
@property float startProgress;
@property float endProgress;
@end
2.9.2 属性介绍
过渡动画有type和subtype两个属性,type用于指定动画类型,subtype用于指定动画移动的方向。
- type属性是一个NSString类型,用于控制整体动画效果类型,具体的可选类型如下:
type值 | 动画效果 | 对应常量 | 是否支持方向 |
---|---|---|---|
fade | 默认效果,渐变 | kCATransitionFade | 否 |
moveIn | 覆盖 | kCATransitionMoveIn | 是 |
Push | 退出 | kCATransitionPush | 是 |
Reveal | 揭开 | kCATransitionReveal | 是 |
cube | 立方体 | 无(私有类型) | 是 |
suckEffect | 收缩 | 无(私有类型) | 否 |
oglFlip | 翻转 | 无(私有类型) | 是 |
rippleEffect | 水波动画 | 无(私有类型) | 否 |
pageCurl | 页面揭开 | 无(私有类型) | 只支持左右方向 |
vpageUnCurl | 放下页面 | 无(私有类型) | 只支持左右方向 |
cameraIrisHollowOpen | 镜头打开 | 无(私有类型) | 否 |
cameraIrisHollowClose | 镜头关闭 | 无(私有类型) | 否 |
目前为止,我们只能使用type的前四种公开属性,但是我们可以直接赋值字符串使用:
transition.type = @"cube";
- subtype属性也是一个NSString类型,用于控制动画方向,具体的可选类型如下:
Subtype类型 | 具体描述 |
---|---|
kCATransitionFromRight | 从右向左 |
kCATransitionFromLeft | 从左向右 |
kCATransitionFromTop | 从上向下 |
kCATransitionFromBottom | 从下向上 |
2.9.3 示例
在更改imageView图片的时候加个转场动画
@interface ViewController ()
@property (nonatomic, strong) UIImageView *childView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 执行动画的view
UIImageView *childView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 100, 100)];
childView.image = [UIImage imageNamed:@"qq"];
[self.view addSubview:childView];
_childView = childView;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
_childView.image = [UIImage imageNamed:@"wx"];
CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.duration = 2;
transition.subtype = kCATransitionFromRight;
[_childView.layer addAnimation:transition forKey:nil];
}
@end
注意:和属性动画不同,对指定图层一次只能使用那一次CATransition,因此无论对动画的键设置为什么值,过渡动画都会对它的键设置为”transition”,也就是常量KCATransition.
2.10 自定义过渡动画
过渡动画的过程就是对原始图层外观截图,然后添加一段动画,平滑过渡到图层改变之后的那个截图效果。如果我们知道如何对图层截图,我们就可以使用属性动画来自定义CATransition动画了。
CALayer有一个-renderInContenxt:方法,通过它可以将图层绘制到Core Graphics的上下文中捕获当前内容的图片;所以现在我们尝试这样的实现:对当前视图控制器View进行截图,然后在改变其背景色的时候对截图快速旋转并且淡出,以达到一种过渡的效果;具体的代码示例如下:
- (void)performAnimation{
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
UIView *coverView = [[UIImageView alloc] initWithImage:coverImage];
coverView.frame = self.view.bounds;
[self.view addSubview:coverView];
//使用自定义方法得到随机颜色(切换后的颜色)
UIColor *randomColor = [UIColor randomColor];
self.view.backgroundColor = randomColor;
//使用UIView动画方法来代替属性动画(为了简化代码步骤)
[UIView animateWithDuration:1 animations:^{
CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01);
transform = CGAffineTransformRotate(transform, M_PI_2);
coverView.transform = transform;
coverView.alpha = 0.0;
} completion:^(BOOL finished) {
[coverView removeFromSuperview];
}];
}
效果如下:
注意:-renderInContext:捕获了图层的图片和子图层,但是不能对子图层正确的处理变换效果,而且对视频和OpenGL内容也不起作用。但是使用CATransition,或者使用私有的截屏方式就没有这个限制了。
3. 在动画过程中取消动画
在使用动画的过程中,我们可能需要适时的移除不要的动画,否则就可能造成内存的泄漏问题;从图层中取消动画的方法有以下两种方式:
//方法1:取消指定动画
/* Remove any animation attached to the layer for 'key'. */
- (void)removeAnimationForKey:(NSString *)key;
//方法2:移除所有动画
/* Remove all animations attached to the layer. */
- (void)removeAllAnimations;
关于移除动画的几点说明:
1.动画一旦被移除,图层的外观就立刻更新到当前的模型图层的值;
2.动画通常默认结束之后被自动移除,除非设置了removeCompletion为NO;
3.动画若设置为结束之后不自动移除,那么我们在不需要的时候需手动移除,否则它会一直在内存中,直到图层被销毁。
参考文章:
https://www.jianshu.com/p/c22918a5e7ca