本章介绍图层的几何组成部分,以及他们之间的相互关系,同时介绍如何变换矩阵可以产生复杂的视觉效果。
图层的坐标系
图层的坐标系在不同平台上面具有差异性。在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)则刚好是图层的中心点。
指定图层的几何
虽然图层和图层树与视图和视图的结构在很多方面具有相似性,但是图层的几何却不同,它更加简单通俗。图层的所有几何属性,包括图层的矩阵变换,都可以隐式和显式动画。
下图显示可以在上下文中指定图层几何的属性:
图层的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
):
anchorPoint
默认值是(0.5,0.5),位于图层边界的中心点(如上图显示),B 点把anchorPoint
设置为(0.0,0.5)。最后 C 点(1.0,1.0)把图层的position
设置为图层frame
的右下角。该图适用于iOS
的图层。在 Mac OS X
系统里面,图层使用不同的坐标系,相应的(0.0,0.0)位于左下角,而(1.0,0.0)位于右下角。
图层的 frame、bounds、position 和 anchorPoint 关系如下图所示:
在该示例中,anchorPoint
默认值为(0.5,0.5),位于图层的中心点。图层的position
值为(100.0,100.0),bounds
为(0.0,0.0,120.0,80.0)。通过计算得到图层的frame
为(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),位于图层的左下角位置。
图层的frame
值同样为(40.0,60.0,120.0,80.0),bounds
的值不变,但是图层的position
值已经改变为(40.0,60.0)。
图层的几何外形和Cocoa
视图另外一个不同地方是,你可以设置图层的一个边角的半径来把图层显示为圆角。图层的cornerRadius
属性指定了重绘图层内容,剪切子图层,绘制图层的边界和阴影的时候时候圆角的半径。
图层的zPosition
属性值指定了该图层位于 Z 轴上面的置,zPosition
用于设置图层相对于图层的同级图层的可视位置。
图层的几何变换
图层一旦创建,你就可以通过矩阵变换来改变一个图层的几何形状。CATransform3D
的数据结构定义一个同质的三维变换(4 x 4 CGFloat
值的矩阵),用于图层的旋转、缩放、偏移、歪斜和应用的透视。
图层的两个属性指定了变换矩阵:transform
和sublayerTransform
属性。图层的transform
属性指定的矩阵结合图层的anchorPoint
属性作用于图层和图层的子图层上面。图层的sublayerTransform
属性指定的矩阵只会影响图层的子图层,而不会对 图层本身产生影响。
你可以通过以下的任何一个方法改变CATransform3D
的数据结构:
使用CATransform3D函数
直接修改数据结构的成员
使用键-值编码改变键路径
CATransform3DIdentity
是单位矩阵,该矩阵没有缩放、旋转、歪斜、透视。把该矩阵应用到图层上面,会把图层几何属性修改为默认值。
变换函数
使用变换函数可以在核心动画里面操作矩阵。你可以使用这些函数(如下表)去创建一个矩阵一般后面用于改变图层或者它的子图层的transform
和sublayerTransform
属性。变换函数或者直接操或者返回一个CATransform3D
的数据结构。这可以让你能够构建简单或复杂的转换,以便重复使用。
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; tx ty 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]. |
CSTransfoem3DScale | Scale ‘t’ by ‘(sx, sy, sz)’ and return the result: * t’ = scale(sx, sy, sz) * t. |
CDTransform3DMakeRotation | Returns a transform that rotates by ‘angle’ radians about the vector ‘(x, y, z)’. If the 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 DegreesToRadians(CGFloat degrees) {
return degrees * M_PI / 180;
};

CGFloat RadiansToDegrees(CGFloat radians) {
return radians *180/M_PI;
};
核心动画提供了用于转换矩阵的变换函数CATransform3DInvert
。一般是用反转点内转化对象提供反向转换。当你需要恢复一个已经被变换了的矩阵的时候,反转将会非常有帮助。反转矩阵乘以逆矩阵值,结果是原始值。
变换函数同时允许你把CATransform3D
矩阵转化为CGAffineTransform
(仿射) 矩阵,前提是CATransform3D
矩阵采用如下表示方法。
Function | Use |
---|---|
CATransform3DMakeAffineTransform | Returns a CATransform3D with the same effect as the passed affine transform. |
CATransform3DIsAffine | Returns YES if the passedCATransform3D can be exactly represented an affine transform. |
CATransform3DGetAffineTransform | Returns the affine transform represented by the passedCATransform3D. |
变换函数同时提供了可以比较一个变换矩阵是否是单位矩阵,或者两个矩阵是否相等。
Function | Use |
---|---|
CATransform3DIsIdentity | Returns YES if the transform is the identity transform. |
CATransform3DEqualToTransform | Returns YES if the two transforms are exactly equal.. |
修改变换的数据结构
你可以修改CATransform3D
的数据结构的元素为任何其他你想要的数据值。上述表格1 包含了CATransform3D
数据结构的定义,结构的成员都在其相应的矩阵位置。
struct CATransform3D

{

CGFloat m11, m12, m13, m14;

CGFloat m21, m22, m23, m24;

CGFloat m31, m32, m33, m34;

};

typedef struct CATransform3D CATransform3D
表格 2 中的示例说明了如何配置一个CATransform3D
一个角度变换。

CATransform3D aTransform = CATransform3DIdentity;

// the value of zDistance affects the sharpness of the transform.

zDistance = 850;

aTransform.m34 = 1.0 / - zDistance;
通过键值路径修改变换
核心动画扩展了键-值编码协议,允许通过关键路径获取和设置一个图层的CATransform3D
矩阵的值。 下图描��述了图层的transform
和sublayerTransform
属性的相应关键路径。
你不可以通过 Objective-C 2.0 的属性来设置结构域的值,比如下面的代码将会无法正常运行:
myLayer.transform.rotation.x=0;
替换的办法是,你必须通过setValue:forKeyPath:
或者 valueForKeyPath:
方法,具体如下:
[myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];