Core Animation 是一个图形渲染和动画基础设施,可在 iOS 和 OS X 上使用,用于为应用程序的视图和其他视觉元素设置动画。使用 Core Animation,绘制动画每一帧所需的大部分工作都已为您完成。您所要做的就是配置一些动画参数(例如起点和终点)并告诉 Core Animation 开始。Core Animation 完成剩下的工作,将大部分实际绘图工作交给板载图形硬件以加速渲染。这种自动图形加速可产生高帧率和流畅的动画,而不会增加 CPU 负担和减慢应用程序的速度。
类结构
如图所示,CAAnimation
作为虚基类实现了CAMediaTiming
协议(CAAction
协议)。
CAAnimation
共有三个子类CAPropertyAnimation
(属性动画)、CAAnimationGroup
(组动画)、CATransition
(渐变动画)。
CAPropertyAnimation
有两个子类CABasicAnimation
(基础动画)、CAKeyFrameAnimation
(关键帧动画)。
iOS9.0+以后,核心动画又加入了CASpringAnimation
(弹性动画),继承自CABasicAnimation
。
开发者不能直接使用CAAnimation
及CAPropertyAnimation
类,使用核心动画时使用CABasicAnimation
、CAKeyFrameAnimation
、CAAnimationGroup
、CATransition
、CASpringAnimation
类。
CAMediaTiming协议
CAMediaTiming 协议中定义了时间,速度,重复次数等。
/* 用来设置动画延时,若想延迟n秒,就设置为CACurrentMediaTime()+n,
CACurrentMediaTime()为图层当前时间。
默认0。*/
@property CFTimeInterval beginTime;
/* 动画的持续时间. 默认0. */
@property CFTimeInterval duration;
/* 动画速率,决定动画时间的倍率。
当speed为2时,动画时间为设置的duration的1/2。
默认1. */
@property float speed;
/* 动画时间偏移量。
这种方法的一个用途是通过设置“速度”为零和“偏移”为一个合适的值来“暂停”一个图层
默认 0. */
@property CFTimeInterval timeOffset;
/* 动画的重复次数。
可能是部分。
默认 0. */
@property float repeatCount;
/* 动画的重复持续时间.
此属性优先级大于repeatCount.
也就是说如果repeatDuration设置为1秒重复10次,那么它会在1秒内执行完动画.
默认 0. */
@property CFTimeInterval repeatDuration;
/* 动画由初始值到最终值后,是否反过来回到初始值的动画。
如果设置为YES,就意味着动画完成后会以动画的形式回到初始值。动画时间增加一倍。
默认NO*/
@property BOOL autoreverses;
/* 定义定时对象在其活动持续时间之外的行为。
本地时间可以固定在活动持续时间的任意一端,或者元素可以从表示中删除。
默认为: kCAFillModeRemoved
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 这个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,将一个动画加入一个layer的时候延迟5秒执行.然后就会发现在动画没有开始的时候,只要动画被加入了layer,layer便处于动画初始状态
kCAFillModeBoth 理解了上面两个,这个就很好理解了,这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.
*/
@property(copy) CAMediaTimingFillMode fillMode;
- beginTime:刚才上面简单解释了下这个属性的用法:CACurrentMediaTime()+ x 会使动画延迟执行x秒.不知道到这里有没有人想过如果-x会出现怎么样效果?假设我们有执行一个3秒的动画,然后设置beginTime = CACurrentMediaTime()- 1.5那么执行动画你会发现动画只会执行后半段,也就是只执行后面的3-1.5s的动画.为什么会这样?其实动画都有一个timeline(时间线)的概念.动画开始执行都是基于这个时间线的绝对时间,这个时间和它的父类有关(系统的属性注释可以看到).默认的CALayer的beginTime为零,如果这个值为零的话,系统会把它设置为CACurrentMediaTime(),那么这个时间就是正常执行动画的时间:立即执行.所以如果你设置beginTime=CACurrentMediaTime()+x;它会把它的执行时间线推迟x秒,也就是晚执行x秒,如果你beginTime=CACurrentMediaTime()-x;那它开始的时候会从你动画对应的绝对时间开始执行.
- timeOffset:时间偏移量,默认为0;既然它是时间偏移量,那么它即和动画时间相关.这么解释:假设我们设置一个动画时间为5s,动画执行的过程为1->2->3->4->5,这时候如果你设置timeOffset = 2s那么它的执行过程就会变成3->4->5->1->2如果你设置timeOffset = 4s那么它的执行过程就会变成5->1->2->3->4
//www.greatytc.com/p/3f48fabaca19
CAAnimation
CAAnimation 核心动画基础类,不能直接使用。
/* Creates a new animation object. */
+ (instancetype)animation;
/* Animations implement the same property model as defined by CALayer.
* See CALayer.h for more details. */
+ (nullable id)defaultValueForKey:(NSString *)key;
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
/* 控制动画的节奏。系统提供的包括:
kCAMediaTimingFunctionLinear (匀速,默认),
kCAMediaTimingFunctionEaseIn (慢进快出),
kCAMediaTimingFunctionEaseOut (快进慢出),
kCAMediaTimingFunctionEaseInEaseOut (慢进慢出,中间加速),
kCAMediaTimingFunctionDefault ,
当然也可通过自定义创建CAMediaTimingFunction。 */
@property(nullable, strong) CAMediaTimingFunction *timingFunction;
/* 代理
注意:此处代理为强引用*/
@property(nullable, strong) id <CAAnimationDelegate> delegate;
/* 是否让图层保持显示动画执行后的状态。
默认为YES,也就是动画执行完毕后从涂层上移除,恢复到执行前的状态。
如果设置为NO,并且设置fillMode为kCAFillModeForwards,则保持动画执行后的状态。*/
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
CAAnimationDelegate
代理为强引用,需移除,否则会引起内存泄露。
@optional
/*当动画开始其活动持续时间时调用。*/
- (void)animationDidStart:(CAAnimation *)anim;
/* 当动画完成其活动持续时间或从它所附加的对象(即图层)中移除时调用。
如果动画到达活动持续时间的结束而没有被删除,则'flag'为true。
动画期间页面切换或进入后台时也会调用,则'flag'为NO */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
CAPropertyAnimation
CAPropertyAnimation 属性动画,针对对象的可动画属性进行效果的设置,不可直接使用。
/* Creates a new animation object with its `keyPath' property set to
* 'path'. */
+ (instancetype)animationWithKeyPath:(nullable NSString *)path;
/* CALayer的某个属性名,并通过这个属性的值进行修改,达到相应的动画效果。 */
@property(nullable, copy) NSString *keyPath;
/* 属性动画是否以当前动画效果为基础,默认为NO。 */
@property(getter=isAdditive) BOOL additive;
/* 指定动画是否为累加效果,默认为NO */
@property(getter=isCumulative) BOOL cumulative;
/* 此属性配合CALayer的transform属性使用。 Defaults to nil. */
@property(nullable, strong) CAValueFunction *valueFunction;
可设置属性动画的属性:CALayer 动画属性
opacity 透明度
backgroundColor 背景颜色
cornerRadius 圆角
borderWidth 边框宽度
contents 内容
shadowColor 阴影颜色
shadowOffset 阴影偏移量
shadowOpacity 阴影透明度
shadowRadius 阴影圆角
...
rotation 旋转
transform.rotation.x
transform.rotation.y
transform.rotation.z
...
scale 缩放
transform.scale.x
transform.scale.y
transform.scale.z
...
translation 平移
transform.translation.x
transform.translation.y
transform.translation.z
...
position 位置
position.x
position.y
...
bounds
bounds.size
bounds.size.width
bounds.size.height
bounds.origin
bounds.origin.x
bounds.origin.y
CABasicAnimation
/* 定义要在其中插入的属性值的对象。
*所有都是可选的,并且不应该超过两个非空值。对象类型应该与动画属性的类型匹配(使用CALayer.h中描述的标准规则)。支持的动画模式有:
*
* - ' fromValue'和' toValue'都非空。在' fromValue'和' toValue'之间插入。
*
* - ' fromValue'和' byValue'非nil。在' fromValue'和' fromValue' + ' byValue'之间插入。
*
* - ' byValue'和' toValue'非nil。在“toValue”减去“byValue”和“toValue”之间插入。
*
* - ' fromValue'非nil。在“fromValue”和属性的当前表示值之间插入。
*
* - ' toValue'非nil。在渲染树中属性的当前值和“toValue”之间插入。
*
* - ' byValue'非nil。在渲染树中属性的当前值和那个加上' byValue'之间插入。*/
@property(nullable, strong) id fromValue;
@property(nullable, strong) id toValue;
@property(nullable, strong) id byValue;
动画添加
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
basicAnimation.fromValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
basicAnimation.toValue =[NSValue valueWithCGSize:CGSizeMake(200, 200)] ;
basicAnimation.duration = 5;
basicAnimation.removedOnCompletion = NO;
basicAnimation.fillMode = kCAFillModeForwards;
[self.animationLayer addAnimation:basicAnimation forKey:@"animationLayerAnimation"];
注意
basicAnimation.removedOnCompletion = NO;
basicAnimation.fillMode = kCAFillModeForwards;
有意思的是添加两行代码后当动画过程中应用切到后台或者其他页面(代理NO),动画并不会被打断(结束无代理通知)。视图移除后(代理NO)再添加视图,视图动画继续(结束无代理通知)。
如果不添加进行上述操作后则会回到动画设置之前状态,动画打断并回调代理(代理NO)。
.When my application is entering background, because the user push the home button, the animations correctly set in pause, but when i re-open my app, the animations have disappeard.How could i fix it please ?
当我的应用进入了后台,因为用户按了home键,动画被设置成了暂停,但当我重新打开应用时,动画都消失了,我如何修复它?
This is correct and built-in behavior. When you leave the app, all animations are removed from their layers: the system calls removeAllAnimations on every layer.
你的情况是系统默认的行为.当你离开了应用后(比如进入了后台),所有的动画都从他们的layer上移除了:因为系统调用了removeAllAnimations,针对所有的layer.
参考文档:iOS CAlayer 动画进入后台或者 调用viewWillDisappear viewDidDisappear后动画暂停问题的解决办法
循环动画应在进入后台或切换至其它页面时移除动画(系统已处理,代理为NO不再添加即可),进入前台或切回本页面时重新开启动画。
动画移除
- (void)removeAllAnimations;
- (void)removeAnimationForKey:(NSString *)key;
动画移除后回到初始状态,代理NO
动画暂停
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
动画恢复
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
CASpringAnimation
/* 系在弹簧末端的物体的质量。必须大于0。默认为1。 */
@property CGFloat mass;
/* 弹簧刚度系数。必须大于0。
*默认为100。 */
@property CGFloat stiffness;
/* 阻尼系数。必须大于等于0。
*默认为10。*/
@property CGFloat damping;
/* 弹簧上物体的初速度。默认值为零,表示一个不移动的对象。
负值表示物体远离弹簧附着点,正值表示物体向弹簧附着点移动。
*/
@property CGFloat initialVelocity;
/* 返回弹簧系统被认为处于静止状态所需的估计持续时间。
持续时间为当前动画参数计算。
*/
@property(readonly) CFTimeInterval settlingDuration;
CAKeyframeAnimation
/* 一个对象数组,为每个关键帧提供动画函数的值。*/
@property(nullable, copy) NSArray *values;
/* 一个可选的路径对象,定义动画函数的行为。
当non-nil覆盖' values'属性时。
除了“moveto”点外,路径上的每个点都定义了一个用于计时和插值的关键帧。
默认为零。
对于沿路径匀速动画,' calculationMode'应该设置为'paced'。
在赋值时,将复制路径。 */
@property(nullable) CGPathRef path;
/* 一个可选的' NSNumber'对象数组,定义了动画的节奏。
每次都对应于“values”数组中的一个值,并定义了该值应该在动画函数中使用的时间。
数组中的每个值都是一个范围为[0,1]的浮点数。
里面的值后一个比前一个要大或者相等。
当keyTimes没有设置的时候,各个关键帧的时间是平分的
*/
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
/* CAMediaTimingFunction对象的可选数组。
如果values数组定义了n个关键帧,那么在timingFunctions数组中应该有n-1个对象。
每个函数描述一个关键帧到关键帧段的节奏。 */
@property(nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions;
/* "计算模式".
默认值: kCAAnimationLinear.有以下几个值:
//kCAAnimationLinear//关键帧为座标点的时候,关键帧之间直接直线相连进行插值计算;
//kCAAnimationDiscrete//离散的,也就是没有补间动画
//kCAAnimationPaced//平均,keyTimes跟timeFunctions失效
//kCAAnimationCubic对关键帧为座标点的关键帧进行圆滑曲线相连后插值计算,对于曲线的形状还可以通过tensionValues,continuityValues,biasValues来进行调整自定义,keyTimes跟timeFunctions失效
//kCAAnimationCubicPaced在kCAAnimationCubic的基础上使得动画运行变得均匀,就是系统时间内运动的距离相同,,keyTimes跟timeFunctions失效 */
@property(copy) CAAnimationCalculationMode calculationMode;
/* 对于立方计算模式的动画,这些属性提供了对插值方案的控制。
每个关键帧可能都有一个张力、连续性和偏差值,每个值都在[- 1,1]范围内(这定义了Kochanek-Bartels样条,见http://en.wikipedia.org/wiki/Kochanek-Bartels_spline)。
*张力值控制曲线的“紧度”(正值更紧,负值更圆)。
连续值控制分段的连接方式(正值表示尖角,负值表示倒角)。
偏差值定义了曲线发生的位置(正值将曲线移动到控制点之前,负值将曲线移动到控制点之后)。
*
*每个数组中的第一个值定义到第一个控制点的切线行为,第二个值控制第二个点的切线,以此类推。
任何未指定的值默认为零(如果所有值都未指定,则给出Catmull-Rom样条)。*/
@property(nullable, copy) NSArray<NSNumber *> *tensionValues;
@property(nullable, copy) NSArray<NSNumber *> *continuityValues;
@property(nullable, copy) NSArray<NSNumber *> *biasValues;
/* 动画沿路径旋转方式,默认为nil.它有两个值:
//kCAAnimationRotateAuto//自动旋转,
//kCAAnimationRotateAutoReverse//自动翻转 */
@property(nullable, copy) CAAnimationRotationMode rotationMode;
values & keyTimes
CAKeyframeAnimation *positionKeyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
positionKeyAnimation.values = @[[NSValue valueWithCGPoint:CGPointMake(150, 100+CGRectGetHeight(view.bounds)/2.f)],
[NSValue valueWithCGPoint:CGPointMake(150, 50)],
[NSValue valueWithCGPoint:CGPointMake(150, 50)],
[NSValue valueWithCGPoint:CGPointMake(150, -CGRectGetHeight(view.bounds)/2.f)]];
positionKeyAnimation.keyTimes = @[@0.25,@0.5,@0.75,@1];
positionKeyAnimation.duration = 10;
positionKeyAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
positionKeyAnimation.removedOnCompletion = NO;
positionKeyAnimation.fillMode = kCAFillModeForwards;
[self.animationLayer addAnimation:groupAnimation forKey:@"positionKeyAnimation"];
CAAnimationGroup
CAAnimationGroup 动画组,方便对于多动画的统一控制管理。
/* 一个CAAnimation对象的数组。
数组的每个成员将使用常规规则在父动画的时间空间中并发运行。 */
@property(nullable, copy) NSArray<CAAnimation *> *animations;
设置组动画时设置子动画beginTime,该子动画失效;
组动画属性设置优先级高于子动画。
CAKeyframeAnimation *positionKeyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
positionKeyAnimation.values = @[[NSValue valueWithCGPoint:CGPointMake(150, 100+CGRectGetHeight(view.bounds)/2.f)],
[NSValue valueWithCGPoint:CGPointMake(150, 50)],
[NSValue valueWithCGPoint:CGPointMake(150, 50)],
[NSValue valueWithCGPoint:CGPointMake(150, -CGRectGetHeight(view.bounds)/2.f)]];
positionKeyAnimation.keyTimes = @[@0.25,@0.5,@0.75,@1];
positionKeyAnimation.duration = 10;
positionKeyAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
CAKeyframeAnimation *opacityKeyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
opacityKeyAnimation.values = @[[NSNumber numberWithInt:0],[NSNumber numberWithInt:1],[NSNumber numberWithInt:1],[NSNumber numberWithInt:0]];
opacityKeyAnimation.keyTimes = @[@0.25,@0.5,@0.75,@1];
opacityKeyAnimation.duration = 10;
opacityKeyAnimation.timingFunctions = positionTimingFunctions;
//组动画
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.beginTime = CACurrentMediaTime()+5;
groupAnimation.duration = 10;
groupAnimation.removedOnCompletion = NO;
groupAnimation.fillMode = kCAFillModeForwards;
groupAnimation.delegate = self;
groupAnimation.autoreverses = NO;
groupAnimation.animations = [NSArray arrayWithObjects:positionKeyAnimation,opacityKeyAnimation,nil];//将所有动画添加到动画组
//添加动画
[view.layer addAnimation:groupAnimation forKey:@"bgViewGroupAnimation"];
CATransition
渐变动画。
/* 转换的名称。
当前合法的过渡类型包括“fade”,“moveIn”,“push”和“reveal”。
默认为“fade”。*/
@property(copy) CATransitionType type;
/* 转换的可选子类型。
例如,用于指定基于运动的过渡的过渡方向,
在这种情况下,合法的值是' fromLeft', ' fromRight', ' fromTop'和' fromBottom'。 */
@property(nullable, copy) CATransitionSubtype subtype;
/*从开始执行到结束执行的过渡的进度量。
合法的值是范围为[0,1]的数字。
' endProgress'必须大于等于' startProgress'。
默认值分别为0和1。*/
@property float startProgress;
@property float endProgress;
参考文档
iOS动画篇_CoreAnimation(超详细解析核心动画)
iOS 动画篇(一) Core Animation
iOS动画详解(学习动画看这一篇就够了)
iOS动画-通篇详解
CAKeyframeAnimation(一)使用和属性全面解析
CAKeyframeAnimation—关键帧动画