3. Layer Geometry
Let no one unversed in geometry enter here.
——Sign above the entrance to Plato's Academy
UIView 有三个基本的布局属性:frame,bounds 和 center。CALayer 也有 frame,bounds 和 position 三个对应的属性。frame 代表 layer 的外部坐标,bounds 代表内部坐标({0,0}通常相当于layer 的左上角,个别情况除外),center 和 position 一样,代表 anchorPoint 相对 superlayer 的位置。
view 的 frame,bounds 和 center 实际上就是对其隐藏在背后的 layer 的对应属性的封装。当你在控制 view 的 frame 的时候,实际上你是在改变 layer 的 frame。
- 值得注意的是,当一个 layer 被旋转或者缩放时,它的 frame 变成了 transformed layer 所占据的一片轴向对齐的矩形区域,这也就意味着 frame 的 width 和 height 可能跟 bounds 不匹配了。
anchorPoint 属性决定着 layer 的 frame 相对于其 position的位置。你可以把 anchorPoint 当做是移动 layer 的手柄。默认情况下,anchorPoint 位于 layer 的中心(也就是与中心重合),因此 layer 也是以该点的位置做为中心摆放的。
在 UIView 中并没有暴露 anchorPoint 属性的接口,这也就是为什么 view 的“中点”属性叫做 center 而不是 position 的原因。
一个 layer 的 anchorPoint 是可以被移动的,当你把它放到 layer 的 frame 左上角时,layer 的内容(content)将会被挪到其 position 的右下方。
像第二章中介绍的 contentsRect 和 contentsCenter 属性一样,anchorPoint 是以单元坐标(相对坐标)为单位的,也就是说,它的坐标相对于 layer 的尺寸是相对的。在 layer 的左上角时是{0,0},在右下角时是{1,1},默认值是在中心{0.5,0.5}。当 anchorPoint 的值小于 0 或者大于 1 时,anchorPoint 就在 layer 的边界(bounds)外了。
当我们改变 anchorPoint 时,position (中心)属性不会发生改变,但是 frame 会跟着变化。
那么,我们为什么需要改变 anchorPoint 的值呢?我们完全可以通过设置 frame 来达到我们想要的位置,改变 anchorPoint 的值话不会反而引起困惑吗?有个场景可以回答这个问题——当 layer 在变换(旋转、缩放)过程中,是以 position 的位置为中心的,所以这时 anchorPoint 就可以发挥作用了。可以参考一下时钟的 demo。
layers 和 views 一样,也是按层级来放置的,layer 的 position 就是相对于它的 superlayer 的 bounds 的。如果 superlayer 移动了,那么其所有 sublayer 也跟着移动。
可能有时候你想知道一个 layer 的绝对位置或者它相对于某一 layer 的位置而非它直接的 superlayer 的位置。CALayer 提供了一些在不同坐标系中转换坐标的方法:
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
- 在 iOS 中,一个 layer 的 position 是以其 superlayer 的左上角为基准的;而在 Mac OS 上,一个 layer 的 position 是以其 superlayer 的左下角为基准的。Core Animation 提供了 geometryFlipped 属性来翻转 layer 在 superlayer 中的垂直方向上的位置。
UIView 只有二维的空间,CALayer 存在三维的空间。除了前面讨论过的 position 和 anchorPoint 属性之外,CALayer 还有 zPosition 和 anchorPointZ 这两个属性,这两个属性都是浮点数值,用来描述layer 在 z 轴方向上的位置。
除了在变换(transform)时要用到 zPosition 外,你唯一可能用到 zPosition 的实际场景是改变你的 layer 的展示层级顺序。通常 layer 是根据其在 superlayer 的 sublayers 数组中的出现的顺序来绘制的,这种算法被称为“画家算法”(painter's algorithm)。通过增大一个 layer 的 zPosition 的值,你可以将其移到其他 zPosition 值更小的 layer 的前面去。值得注意的是,zPosition 仅仅只能改变 layer 的展示顺序,并不能改变 layer 在其 superlayer 的 sublayers 数组中的顺序。
-containsPoint:
方法可以用来判断一个点(point)是否在该 layer 的 frame 中。-hitTest:
方法接受一个 CGPoint 参数,返回一个 frame 包含了该点(point)的最深层的 sublayer 或者其本身,如果该 layer 和 sublayer 的 frame 都不包含改点,则返回 nil。