主要内容
- Core Animation 简介
- CALayer
- CALayer 简介
- 层与视图的关系
- CALayer 的使用
- 层的显示内容
- 隐式可动画属性
- 层、位图与上下文
- CAAnimation
- CAAnimation 简介
- 通过CABasicAnimation 实现旋转动画
- 通过CAKeyframeAnimation 实现弹跳动画
- 外观层和模型层
Core Animation 简介
- 动画重要性:在 iOS 里使用恰当的动画,能给予用户视觉线索,更好地了解应用的工作流程,提升用户体验。
- 使用 Core Animation :QuartzCore.framework
- Core Animation 两个主要的类是 CALayer 和 CAAnimation
CALayer
CALayer 简介
- CALayer 实例本质是一块包含一幅位图(bitmap)的缓冲区。
- 位图图像(bitmap),亦称为点阵图像或绘制图像,是由称作像素(图片元素)的单个点组成的。
- 位图与矢量图的比较
层与视图的关系
创建视图实例时,视图会自己创建一个层(隐式层)。视图在绘图时,会将内容画在自己的层上。
- 根据每个像素的不透明度(opacity),将一组层按照特定的顺序拷贝至屏幕。
- 视图先在层上完成绘图,然后系统会将所有的层合成至屏幕。
为什么要将视图和层分成两个概念?
UIView是UIResponder的子类,是用类封装起来的一个抽象概念,用来描述某种“可视对象”。通过屏幕,这类对象可以和用户交互。
CALayer 只和绘图,内容显示有关。
CALayer 的使用
显式层:通过 CALayer 类发送alloc 创建。
对UIView,可以通过定义视图的frame属性,设置相应的大小和位置。frame 矩形的起始点(origin)是视图的左上角,再从起始点开始,根据大小向右和向下延伸。
对CALayer,一般不是定义frame,而是设置bounds和position
position的默认值是父层的center,anchorPoint 决定position在(层的)bounds中的位置,默认值(0.5,0.5),即中心点。如果值修改层的大小,不修改位置,层会保持居中显示。
虽然没有frame,通过发送 frame 和 setFrame:存取frame。当层收到frame消息时,会通过position和bounds计算出相应的矩形区域。当层收到 setFrame:消息时,会执行若干数学运算,然后将相应的值赋给bounds和position
层的显示内容
可以通过代码或 UIImage 设置层的contents属性。
通过代码设置contents属性有两种方法(都要用到Core Graphics):
- 创建CALayer子类。可以根据层的某些状态绘制不同的内容。这种情况下只能选择覆盖 drawInContext:方法。
- 为CALayer实例设置委托对象,然后由委托对象实现绘图(隐式层使用的就是这种方法;视图是它的隐式层的委托对象)隐式层或显示层,都可以使用委托机制。不要将UIView对象设置为显示层的委托对象。这是因为UIview已经是隐式层的委托对象。显示层时,层会向它的委托对象发送 drawLayer:inContext:消息。委托对象可以通过传入的context对象,执行相应的 CoreGraphics调用
QuartzCore框架(包含CALayer和CAAnimation)和Core Graphics (包含CGImageRef)都能在iOS和Mac OS X上使用,但是 UIKit(包含UIImage和其他所有UI开头的类)只能在iOS中使用。
为了保持可移植性,QuartzCore不能使用UIImage,只能使用 CGImageRef。
层是以层次结构的形式存在的,层可以有子层。每个层都有一个指针指回它的父层,这个指针称为superLayer。系统在将某个层合成至屏幕时,会先将这个层拷贝至屏幕,然后依次将其下子层合成在它的上面。任何一个层必定是在它的父层之上绘制的。
在视图层次结构中,兄弟视图(有相同父视图的视图)的bounds属性通常不会重叠,没有意义,即使重叠,只会妨碍用户操作。
层负责的是视觉效果和绘图,兄弟层很可能会重叠。
如何决定兄弟层的合成顺序?层有一个 zPosition。如果两个层是兄弟层并且重叠,zPosition值高的层会在zPosition值低得层之上合成(子层必会在父层之上绘图,和zPosition无关)
隐式可动画属性
部分CALayer属性是隐式可动画的。调用这些属性的存方法时,相应的修改会自动产生动画效果。position属性是隐式可动画属性。
拖拽动画的不连贯区别说明:
通过动画事务可以关闭隐式动画。动画事务可以批量处理动画并设置相应的参数。例如持续时间和动画曲线。
向CATransaction 类发送 begin 消息可以开始一个动画事务。
向CATransaction 类发送 commit 消息可以结束动画事务。
在begin和commit之间,可以统一设置层的属性和CATransaction对象的属性。
层、位图与上下文
层就是一副位图,一保存着每个像素的红、绿、蓝和alpha数值的内存空间。
当UIView实例收到setNeedsDisplay消息后,会将该消息转发给视图的层。当运行循环处理完一个事件后,所有标记为“需要重新显示”的层都会准备一个CGContextRef。在调用绘图函数或绘图方法时,如果传入的上下文参数是CGContextRef,那么生成的像素最终都会体现在这个层的位图中。
- (void)drawRect:(CGRect)r
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
}
就能得到一个CGContextRef指针,指向层的 CGContext 对象。在 drawRect:中完成的所有绘图都会填入层的位图,然后拷贝至屏幕。
绘图函数或绘图方法所生成的像素最终会体现在层的位图中。
UIGraphics系列函数提供了一套简便方案,可以创建位图CGContext对象,并将其写入UIImage对象。
当层重画显示内容时,会创建相同类型的上下文对象。但实现方法稍有不同
CAAnimation
CAAnimation简介
CAAnimation 对象可以在一定时间内持续引发某个数值的变化。通常会通过CAAnimation对象修改层的某个属性(例如,opacity)。
CAAnimation是一组指令(例如,用2秒的时间,从A点移动到B点)
可以将 动画对象(CAAnimation)加入CALayer实例,一旦加入,层就会开始执行动画对象的指令。
CALayer 的很多属性都可以通过CAAnimation实现动画效果,这些属性包括:opacity、position、transform、bounds和contents等
iOS 中的所有动画效果都是由CAAnimation驱动的。
CAAnimation 是所有动画对象的抽象父类。它负责处理速度控制(timing),例如,CAAnimation的duration属性,通过这个属性可以设置动画的持续时间。由于 CAAnimation 是抽象类,所以不能直接使用。这里必需使用CAAnimation的某个具体子类。
CAPropertyAnimation 是 CAAnimation 的子类,通过加入修改层属性的功能,扩充了父类,每个属性动画(property animation)都有一个类型为 NSString 的键路径(key path)。这个字符串是CALayer的可动画属性的属性名。CALayer的很多属性都支持动画效果。
Apple的开发文档列出了完整的列表:查找 animatable properties
建路径通常就是属性的名称。例如,拥有键路径 opacity 的动画属性,能够为层 opacity 属性实现动画效果。
某些属性的类型是 C 结构(例如 position,类型是 CGPoint),通过键路径可以访问这些结构的所有成员,(详见开发文档 Core Animation Extensions To Key-Value Coding)。
和 CAAnimation 一样,CAPropertyAnimation 也是抽象类。要创建能够修改某个层属性的动画对象,需要使用以下两个 CAPropertyAnimation 的具体子类(二选一):CABasicAnimation 和 CAKeyframeAnimation.使用 Core Animation时,会经常用到这两个类。
CABasicAnimation 相对较简单,他有两个属性:fromValue和toValue,还有继承自CAAnimation的duration属性。将 CABasicAnimation加入层后,该对象的fromValue 会被赋值给需要动画的属性。随着动画的进行(持续时间通过duration设定),相应属性的值将线性地从 fromValue 变为 toValue。
CABasicAnimation 和 CAKeyframeAnimation 的区别:CABasicAnimation 只能从一个数值变成另一个数值,而 CAKeyframeAnimation 没有这种限制。CAKeyframeAnimation 会使用 NSArray 对象保存这些数值,并以发生的顺序排列,且它的values属性负责保存这个数组。
values 属性中的对象称为关键帧。动画对象会按指定的时间(duration)带着需要动画的属性,依次显示数组中的每一个关键帧。CABasicAnimation 对象其实是一个最多只能有两个关键帧的 CAKeyframeAnimation 对象(除了支持两个以上的关键帧,CAKeyframeAnimation还可以修改每个关键帧的速度,这里先不做介绍)
另外还有两个 CAAnimation 子类,它们的使用频率相对较少。CAAnimationGroup 实例能够保存一组动画对象。将CAAnimationGroup加入层后,组中所有动画会同时并发运行。
CATransition 能够为层提供移出屏幕和移入屏幕时的动画效果。在 Mac OS X上, CATransition 由功能强大的Core Image Filters 实现;在iOS上,它只能实现少量简单渐变效果,例如:渐隐渐现(fade)和滑入画出(sliding)(UINavigationController就是通过CATransition实现了将是同控制对象的视图推入屏幕时的动画效果)
通过CABasicAnimation 实现旋转动画
速度控制函数:CAMediaTimingFunction
动画结束后的处理:
有时需要知道某个动画会在何时结束,这样就可以在动画结束时执行特定的任务,例如,连续显示动画,或者在动画结束后更新某个对象。
每个动画对象可以有一个委托对象,动画对象会在动画结束时向委托对象发送 animationDidStop:finished:消息
通过CAKeyframeAnimation 实现弹跳动画
速度控制函数:CAMediaTimingFunction
外观层和模型层
可以将CALayer实例想象为两部分:
- 合成至屏幕的图像内容
- 描述如何合成层一组参数:opacity、transform、position等
实际上,对动画中的层,会有两套这样的参数:一套针对模型(model),另一套针对外观(presentation)。外观参数是动画对象持续平滑地修改的那些参数,而模型参数是不变的,即动画结束后真正生效的参数。
对动画中的层,系统会使用外观参数将层的显示内容合成至屏幕。而在和动画无关的情况下,则会使用模型参数。
以上两套参数的正式名称是模型层(the model layer)和外观层(the presentation layer)。
向层查询 position 属性,得到的时模型层的position属性。要获取外观层的属性,需要先得到 presentationLayer 。
在什么情况下会用到外观层的属性?
假设有一款游戏应用会在屏幕上显示动画的对象,当用户按中某个对象时,相应的对象就会爆炸。因为只有外观层才知道对象在屏幕上的当前位置,所以为了能对用户的按下动作做出精确的碰撞测试,就必须知道外观层的属性。
防止出现以下两种常见错误。:
- 动画效果表现正常。但是在动画结束后,层又回到了初始位置(或不透明度,等等),没能按预期保留状态。发生此类问题的原因是没有更新模型参数,并和动画的最终状态匹配。
- 层直接进入最终状态,没有动画效果。启动动画时,如果没有为 fromValue赋值,那么对象会自动地讲模型层的相应属性赋给fromValue。如果在动画开始前模型层更新至了最终状态,就会导致 fromVallue 和 toValue 是相同的值。一般情况下显式地为 fromValue 赋值即能修正此类问题。