第二章 核心动画渲染框架
虽然核心动画的图层和 Cocoa 的视图在很大程度上没有一定的相似性,但是他们两者最大的区别是,图层不会直接渲染到屏幕上。
在模型-视图-控制器(model-view-controller)概念里面 NSView 和 UIView 是典型的视图部分,但是在核心动画里面图层是模型部分。图层封装了几何、时间、可视化属性,同时它提供了图层现实的内容,但是实际显示的过程则不是由它来完成。
每个可见的图层树由两个相应的树组成:一个是呈现树,一个是渲染树。下图显示在 Mac OS X 上面使用核心动画图层类显示一个图层树的例子。
Figure1 CoreAnimationRenderingArchitecture
图层树包含每一层的对象模型值。他们就是你设定的图层的属性值。
呈现树包含了当前动画发生时候将要显示的值,例如你要给图层背景颜色设置新的值的时候,它会立即修改图层树里面相应的值。但是在呈现树里面背景颜色值在将要显示给用户的时候才被更新为新值。
渲染树在渲染图层的时候使用呈现树的值。渲染树负责执行独立于应用活动的复杂操作。渲染由一个单独的进程或线程来执行,使其对应用程序的运行循环影响最小。
在原子动画事务执行过程中,你可以查看一个 CALayer 的实例。如果你打算改变当前的动画,要当前显示的状态开始新的动画,这将会对你有非常大的帮助。
第三章 图层的几何和变换
本章介绍图层的几何组成部分,及他们之间的相互关,同时介绍如何变换矩阵可以产生复杂的视觉效果。
3.1 图层的坐标系
图层的坐标系在不同平台上面具有差异性。在 iOS 系统中,默认的坐标系统原点在图层的中心左上角地方,原点向右和向下为正值。在 Mac OS X 系统中,默认的坐标系原点在图层的中心左下角地方,原点向右和向上为正值。坐标系的所有值都是浮点类型。你在任何平台上面创建的图层都采用该平台默认的坐标系。
每个图层定义并维护自己的坐标系,它里面的全部内容都由此相关的坐标系指定位置。该准则同时适应于图层自己的内容和它的任何子图层。因为任何图层定义了它自己的坐标系,CALayer 类提供相应的方法用于从一个图层坐标系的点、矩形、大小值转化为另一个图层坐标系相应的值。
一些基于图层的属性使用单元坐标空间测量它们的值。单元坐标空间指定图层边界的相对值,而不是绝对值。单元坐标空间给定的 x 和 y 的值总是在 0.0 到 1.0 之间。指定一个沿 X 轴的值为 0.0 的点,得到的是图层左边缘的一个点,而指定一个 1.0的点,则是图层右边缘的一个点。(对 Y 轴而言,如果是在 iOS 系统,则 0.0 对应于顶部的点,而 1.0 则是底部的点,而在 Mac OS X 系统,得到的刚好相反,就如之前提到的坐标系不同一样)。而点(0.5,0.5)则刚好是图层的中心点。
3.2 指定图层的几何
虽然图层和图层树与视图和视图的结构在很多方面具有相似性,但是图层的几何却不同,它更加简单通俗。图层的所有几何属性,包括图层的矩阵变换,都可以隐式和显式动画。
下图显示可以在上下文中指定图层几何的属性:Figure 1 CALayer geometry properties
图层的 position 属性是一个 CGPoint 的值,它指定图层相当于它父图层的位置,该值基于父图层的坐标系。
图层的 bounds 属性是一个 CGRect 的值,指定图层的大小(bounds.size)和图层的原点(bounds.origin)。当你重写图层的重画方法的时候,bounds 的原点可以作为图形上下文的原点。
图层拥有一个隐式的 frame,它是 position,bounds,anchorPoint 和 transform 属性的一部分。设置新的 frame 将会相应的改变图层的 position 和 bounds 属性,但是 frame本身并没有被保存。但是设置新的 frame 时候,bounds 的原点不受干扰,bounds 的大小变为 frame 的大小,即 bounds.size=frame.size。图层的位置被设置为相对于锚点(anchor point)的适合位置。当你设置 frame 的值的时候,它的计算方式和 position、bounds、和 anchorPoint 的属性相关。
图层的 anchorPoint 属性是一个 CGPoint 值,它指定了一个基于图层 bounds 的符合位置坐标系的位置。锚点(anchor point)指定了 bounds 相对于 position 的值,同时也作为变换时候的支点。锚点使用单元空间坐标系表示,(0.0,0.0)点接近图层的原点,而(1.0,1.0)是原点的对角点。改变图层的父图层的变换属性(如果存在的话)将会影响到 anchorPoint 的方向,具体变化取决于父图层坐标系的 Y 轴。
当你设置图层的 frame 属性的时候,position 会根据锚点(anchorPoint)相应的改变,而当你设置图层的 position 属性的时候,bounds 会根据锚点(anchorPoint)做相应的改变。
iOS 注意:以下示例描述基于 Mac OS X 的图层,它的坐标系原点基于左下角。在 iOS 上面,图层的坐标系原点位于左上角,原点向下和向右为正值。这变化用具体数值显示,而不是概念描
述。
下图描述了基于锚点的三个示例值:
Figure 2 Three anchorPoint values
anchorPoint 默认值是(0.5,0.5),位于图层边界的中心点(如上图显示),B 点把 anchorPoint 设置为(0.0,0.5)。最后 C 点(1.0,0.0)把图层的 position 设置为图层 frame 的右下角。该图适用于 Mac OS X 的图层。在 iOS 系统里面,图层使用不同的坐标系,相应的(0.0,0.0)位于左上角,而(1.0,1.0)位于右下角。
图层的 frame、bounds、position 和 anchorPoint 关系如下图所示:Figure 3 Layer Origin of (0.5,0.5)
在该示例中,anchorPoint 默认值为(0.5,0.5),位于图层的中心点。图层的 position值为(100.0,100.0),bounds 为(0.0,0.0,120,80.0)。通过计算得到图层的 frame
Core Animation Programming Guide
为(40.0,60.0,120.0,80.0)。
如果你新创建一个图层,则只有设置图层的 frame 为(40.0,60.0,120.0,80.0),相应的 position 属性值将会自动设置为(100.0,100.0),而 bounds 会自动设置为(0.0,0.0,120.0,80.0)。
下图显示一个图层具有相同的 frame(如上图),但是在该图中它的 anchorPoint属性值被设置为(0.0,0.0),位于图层的左下角位置。
Figure 4 Layer Origin of (0.0,0.0)
图层的 frame 值同样为(40.0,60.0,120.0,80.0),bounds 的值不变,但是图层的position 值已经改变为(40.0,60.0)。
图层的几何外形和 Cocoa 视图另外一个不同地方是,你可以设置图层的一个边角的半径来把图层显示为圆角。图层的 cornerRadius 属性指定了重绘图层内容,剪切子图层,绘制图层的边界和阴影的时候时候圆角的半径。
图层的 zPosition 属性值指定了该图层位于 Z 轴上面位置,zPosition 用于设置图层相对于图层的同级图层的可视位置。
3.3 图层的几何变换
图层一旦创建,你就可以通过矩阵变换来改变一个图层的几何形状。CATransform3D 的数据结构定义一个同质的三维变换(4x4 CGFloat 值的矩阵),用于图层的旋转,缩放,偏移,歪斜和应用的透视。
图层的两个属性指定了变换矩阵:transform 和 sublayerTransform 属性。图层的transform 属性指定的矩阵结合图层的 anchorPoint 属性作用于图层和图层的子图层上面。图 3 显示在使用 anchorPoint 默认值(0.5,0.5)的时候旋转和缩放变换如何影响一
Core Animation Programming Guide
个图层。而图 4 显示了同样的矩阵变换在 anchorPoint 为(0.0,0.0)的时候如何改变一个图层。图层的 sublayerTransform 属性指定的矩阵只会影响图层的子图层,而不会对图层本身产生影响。
你可以通过以下的任何一个方法改变 CATransform3D 的数据结构: 使用CATransform3D函数
直接修改数据结构的成员
使用键-值编码改变键路径CATransform3DIdentity 是单位矩阵,该矩阵没有缩放、旋转、歪斜、透视。把该矩阵应用到图层上面,会把图层几何属性修改为默认值。
3.3.1 变换函数
使用变换函数可以在核心动画里面在操作矩阵。你可以使用这些函数(如下表)去创建一个矩阵一般后面用于改变图层或者它的子图层的 transform 和sublayerTransform属性。变换函数或者直接操或者返回一个CATransform3D的数据结构。这可以让你能够构建简单或复杂的转换,以便重复使用。
Table 1 CATransform3D transform functions for translation, rotation, and scaling
Function
Use
CATransform3DMakeTranslation
Returns a transform that translates by '(tx, ty, tz)'. t' = [1 0 0 0; 0 1 0 0; 0 0 1 0; txty tz 1].
CATransform3DTranslate
Translate 't' by '(tx, ty, tz)' and return the result: * t' = translate(tx, ty, tz) * t.
CATransform3DMakeScale
Returns a transform that scales by `(sx, sy, sz)': * t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0;0 0 0 1].
CATransform3DScale
Scale 't' by '(sx, sy, sz)' and return the result: * t' = scale(sx, sy, sz) * t.
CATransform3DMakeRotation
Returns a transform that rotates by 'angle' radians about the vector '(x, y, z)'. Ifthe vector has length zero the identity transform is returned.
CATransform3DRotate
Rotate 't' by 'angle' radians about the vector '(x, y, z)' and return the result. t' =rotation(angle, x, y, z) * t.
旋转的单位采用弧度(radians),而不是角度(degress)。以下两个函数,你可以在弧度和角度之间切换。
CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180 / M_PI;};
核心动画 提供了用于转换矩阵的变换函数 CATransform3DInvert。一般是用反转点内转化对象提供反向转换。当你需要恢复一个已经被变换了的矩阵的时候,反转将会非常有帮助。反转矩阵乘以逆矩阵值,结果是原始值。
变换函数同时允许你把 CATransform3D 矩阵转化为 CGAffineTransform(仿射)矩阵,前提是 CATransform3D 矩阵采用如下表示方法。
Table 2 CATransform3D transform functions for CGAffineTransform conversion
Function
Use
CATransform3DMakeAffineTransform
Returns a CATransform3D with the same effect as the passed affinetransform.
CATransform3DIsAffine
Returns YES if the passedCATransform3D can be exactly represented anaffine transform.
CATransform3DGetAffineTransform
Returns the affine transform represented by the passedCATransform3D.
变换函数同时提供了可以比较一个变换矩阵是否是单位矩阵,或者两个矩阵是否相等。
Table 3 CATransform3D transform functions for testing equality
3.3.2 修改变换的数据结构
你可以修改 CATransform3D 的数据结构的元素为任何其他你想要的数据值。清单1 包含了 CATransform3D 数据结构的定义,结构的成员都在其相应的矩阵位置。
Listing 1 CATransform3D structure
Function
Use
CATransform3DIsIdentity
Returns YES if the transform is the identity transform.
CATransform3DEqualToTransform
Returns YES if the two transforms are exactly equal..
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
Core Animation Programming Guide
};
typedef struct CATransform3D CATransform3D;
清单 2 中的示例说明了如何配置一个 CATransform3D 一个角度变换。Listing 2 Modifying the CATransform3D data structure directly
3.3.3 通过键值路径修改变换
核心动画扩展了键-值编码协议,允许通过关键路径获取和设置一个图层的CATransform3D 矩阵的值。表 4 描述了图层的 transform 和 sublayerTransform 属性的相应关键路径。
CATransform3D aTransform = CATransform3DIdentity;
// the value of zDistance affects the sharpness of the transform.
zDistance = 850;
aTransform.m34 = 1.0 / -zDistance;
Table 4 CATransform3D key paths
Field Key Path
rotation.x
rotation.y
rotation.z
rotation
scale.x
scale.y
scale.z
scale
translation.x
translation.y
translation.z
translation
Description
The rotation, in radians, in the x axis.
The rotation, in radians, in the y axis.
The rotation, in radians, in the z axis.
The rotation, in radians, in the z axis. This is identical to setting the rotation.z field.Scale factor for the x axis.Scale factor for the y axis.
Scale factor for the z axis.
Average of all three scale factors.
Translate in the x axis.
Translate in the y axis.
Translate in the z axis.
Translate in the x and y axis. Value is an NSSize or CGSize.你不可以通过 Objective-C 2.0 的属性来设置结构域的值,比如下面的代码将会
Core Animation Programming Guide
无法正常运行:
myLayer.transform.rotation.x=0;
替换的办法是,你必须通过 setValue:forKeyPath:或者 valueForKeyPath:方法,具体如下:
[myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];