iOS 动画-CoreAnimation(一)

Core Animation 是一个图形渲染和动画基础设施,可在 iOS 和 OS X 上使用,用于为应用程序的视图和其他视觉元素设置动画。使用 Core Animation,绘制动画每一帧所需的大部分工作都已为您完成。您所要做的就是配置一些动画参数(例如起点和终点)并告诉 Core Animation 开始。Core Animation 完成剩下的工作,将大部分实际绘图工作交给板载图形硬件以加速渲染。这种自动图形加速可产生高帧率和流畅的动画,而不会增加 CPU 负担和减慢应用程序的速度。

类结构

类结构.jpg

如图所示,CAAnimation作为虚基类实现了CAMediaTiming协议(CAAction协议)。
CAAnimation共有三个子类CAPropertyAnimation(属性动画)、CAAnimationGroup(组动画)、CATransition(渐变动画)。
CAPropertyAnimation有两个子类CABasicAnimation(基础动画)、CAKeyFrameAnimation(关键帧动画)。
iOS9.0+以后,核心动画又加入了CASpringAnimation(弹性动画),继承自CABasicAnimation
开发者不能直接使用CAAnimationCAPropertyAnimation类,使用核心动画时使用CABasicAnimationCAKeyFrameAnimationCAAnimationGroupCATransitionCASpringAnimation类。

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;
timingFunction.jpeg

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—关键帧动画

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容