[译] Core Animation 编程指南 - 构建图层层级结构

大多数时间里,使用图层的最好方式就是和视图对象一块儿使用。然而,有时你可能须要经过向视图中添加额外的图层对象来加强视图层次结构。当图层可以提供更好的性能或者仅用视图很难实现的特性时,你可能会使用图层。在这些状况下,你须要知道如何管理你建立的图层的层级结构。html

重要:在 OS X v10.8 及之后的版本,推荐尽量少的使用图层层级结构,尽可能使用图层支持视图。该版本引进的图层重绘协议容许你自定义图层支持视图的行为,而且仍然能得到以前单独使用图层时的性能。数组

将图层分配到图层层级结构中

图层层级结构在不少方面都和视图层级结构类似。你能够将一个图层嵌入到另外一个图层中,给两个图层建立父-子关系。嵌入的图层称为子图层(sublayer),被嵌入的图层称为父图层(superlayer)。这种父子关系影响子图层的多个方面。例如,它的内容在父图层的内容上面,它的位置与它父图层的坐标系相关,应用在父图层上的任何形变也会影响子图层。bash

添加、插入、移除子图层

每一个图层对象都有添加、插入、移除子图层的对象方法。表 4-1 概述了这些方法和它们的行为。app

表 4-1 修改图层层级结构的方法ide

行为 方法 描述
添加图层 addSublayer: 在当前图层上添加一个新的子图层对象。子图层被添加到当前图层的子图层列表的尾部。这将致使子图层出如今 zPosition 属性中具备相同值的任何同级图层的顶部。
插入图层 insertSublayer:above: insertSublayer:atIndex: insertSublayer:below: 将子图层添加到具体索引的位置或者与另外一个子图层相关的位置。当在另外一个子图层上面或者下面插入时,你只是指定了子图层在子图层数组中的位置。图层实际的视觉效果主要由它们的 zPosition 属性的值来决定,其次才由它们在子图层数组的位置决定。
移除图层 removeFromSuperlayer 在父图层上移除子图层。
替换图层 replaceSublayer:with: 将子图层替换为另外一个。若是你插入的子视图已经添加在别的图层层级结构上,它会先从那个层级结构移除。

你能够在本身建立图层对象时使用上述方法。你不该该使用上述方法来分配属于图层支持视图的图层。但,图层支持视图能够当作你本身建立的单独图层的父图层。布局

子图层位置和尺寸的调整

当添加和插入子图层时,在图层显示在屏幕上以前,你必须设置图层的位置和尺寸。在将图层添加到图层层级结构以后,你也能够修改子图层的位置和尺寸。但你应该养成在建立图层时就指定图层的位置和尺寸的值得习惯。post

你可使用 bounds 属性来设置子图层的尺寸,使用 position 属性来设置它在父图层中的位置。矩形边界的起点永远是(0, 0),尺寸则是你给图层以点为单位的任何尺寸。position 属性的值是相对于图层的锚点来定义的,它默认是图层的中心点。若是你不给这些属性赋值,Core Animation 会将图层的宽高初始值设为0,位置的初始值设为(0, 0)。性能

myLayer.bounds = CGRectMake(0, 0, 100, 100);
myLayer.position = CGPointMake(200, 200);
复制代码

重要:始终使用整数表示图层的宽度和高度。动画

图层层级结构如何影响动画

一些父图层的属性能够影响应用在子图层上的任何动画的行为。其中一个属性就是 speed,它是动画速度的乘数。该属性的值默认为1.0,但将它改成2.0会致使动画按起始速度的两倍速来运行,结果就是会提早一半的时间结束。该属性不只影响修改的这一个图层,它也会影响它的子图层。子图层的速度也会加倍。若是父图层和子图层的速度都为2.0,那子图层上的动画会以原始速度的4倍速度来执行。ui

大多数其余图层更改会以可预测的方式影响任何包含的子图层。例如,对图层应用一个旋转形变会致使该图层的全部子图层都会旋转。相似的,改变图层的透明度也会影响子图层的透明度。图层大小的更改遵循的规则,请参见调整图层层级结构的布局

调整图层层级结构的布局

Core Animation 支持多个选项,用来调整子图层的位置和尺寸以响应父图层的改变。在 iOS,图层支持视图的广泛使用使得图层层次结构的建立变得不那么重要;仅支持手动布局更新。在 OS X,其余的可用选项会使图层层级管理变得更加容易。

图层级布局仅与你使用你建立的单独图层对象来构建图层层级结构时相关。若是你的 app 图层都是和视图联合的,使用基于视图的布局支持去更新视图的尺寸和位置来响应改变。

在 OS X 上使用约束去管理你的图层层级结构

约束是你使用图层和父图层或同级图层的一系列详细的关系来制定图层的尺寸和位置。定义约束需遵循一下步骤:

  1. 建立一个或多个 CAConstraint 对象。使用这些对象去定义约束参数。
  2. 将约束对象添加到属性修改的图层上。
  3. 获取共享的 CAConstraintLayoutManager 对象,将它赋值给最近的父图层。

图4-1 展现了你可使用去定义约束的属性,和它影响了图层的方面。你可使用约束,根据中点边缘相对于另外一图层的位置来更改图层的位置。你也可使用它们改变图层的尺寸。你所作的改变能够与父图层成比例,或者和同级图层成比例。你甚至能够将缩放因子或常量添加到结果变化中。这种额外的灵活性使得使用一组简单的规则,便可很是精确地控制层的大小和位置。

图 4-1 约束布局管理属性

每一个约束对象包含两个图层在同一轴线上的几何关系。每一个轴最多能够分配两个约束对象,正是这两个约束决定了哪一个属性是可变的。例如,若是为图层的左边缘和右边缘指定约束,图层的大小会发生变化。若是为图层的左边缘和宽度指定约束,图层右边缘的位置会发生变化。若是为图层的一条边指定单个约束,Core Animation 会建立一个隐式约束,使图层的大小保持固定在给定的尺寸中。

当建立约束时,你必须始终指明这三方面的信息:

  • 要约束的图层的哪一个方面
  • 用哪一个图层看成参考图层
  • 在比较中使用的参考图层的哪一个方面

例4-1 展现了一个约束的例子,该约束表示图层的垂直中心点与父图层的垂直中心点相等。当用父视图作参考图层时,使用 superlayer 字符串。使用它不须要有指向该图层的指针或知道该图层的名称。它也容许你改变父图层,若是你改变父图层,约束会自动应用到新的父图层上。(当建立于同级图层相关联的约束时,你必须使用它的 name 属性来识别同级图层。)

例 4-1 定义简单的约束

[myLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidY]];
复制代码

在运行时应用约束,你必须将共享的 CAConstraintLayoutManager 对象附加给最近的父图层。每一个图层有责任去管理子图层的布局。将布局管理器分配给父级会告诉 Core Animation 应用其子级定义的约束。布局管理对象自动应用约束。将它赋值给父图层以后,你就没必要告诉它去更新布局。

看图 4-2,去了解约束是如何工做的。在该例中,设计要求 layerA 的宽高保持不变,而且 layerA 要位于图图层的中心点。另外, layerB 的宽度必须和 layerA 的宽度相等,layerB 的上边界要距离 layerA 的下边界 10 个点,layerB 的下边界要比父图层的下边界高 10 个点。例 4-2 展现了该例中的代码是如何实现的。

图 4-2 基于布局的约束示例

例 4-2 设置图层的约束

// 建立设置父图层的布局管理
theLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
 
// 建立第一个子图层
CALayer *layerA = [CALayer layer];
layerA.name = @"layerA";
layerA.bounds = CGRectMake(0.0,0.0,100.0,25.0);
layerA.borderWidth = 2.0;
 
// 保持 layerA 的中点与父图层的中点相同
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidY]];
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidX]];
[theLayer addSublayer:layerA];
 
// 建立第二个子图层
CALayer *layerB = [CALayer layer];
layerB.name = @"layerB";
layerB.borderWidth = 2.0;
 
// 使 layerB 的宽度与 layerA 的相同
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintWidth]];
 
//  layerB 和 layerA 水平居中
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintMidX]];
 
// layerB 的上边界距 layerA 的下边界 10 个点
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintMinY
                                                     offset:-10.0]];
 
// `layerB` 的下边界比父图层的下边界高 10 个点
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMinY
                                                     offset:+10.0]];
 
[theLayer addSublayer:layerB];
复制代码

上述代码中有一个有意思的事,它没有显式的设置 layerB 的尺寸。因为定义了约束,每次布局更新,layerB 的宽高都会自动设置。所以,再设置它的宽高是不必的。

警告:当建立约束时,不要在约束中建立循环引用。循环约束使得计算所需的布局信息变得不可能。当遇到这种循环引用时,布局行为是未定义的。

OS X 图层层级结构设置自动调整尺寸规则

在 OS X 中,自动调整尺寸规则是另外一种调整图层尺寸和位置的方法。使用自动调整尺寸规则,你能够指定图层的边缘是应该与父图层的相应边缘保持固定距离仍是可变距离。相似的,你也能够指定图层的宽高是固定的仍是可变的。指定的关系始终是关于图层和其父图层的。你不能将自动调整尺寸规则应用于同级图层。

设置图层的自动调整尺寸规则,你必须给图层的 autoresizingMask 属性赋值合适的常量。图层默认是有固定宽高的。在布局期间,图层精确的尺寸和位置是由 Core Animation 自动为你计算的,这是包含基于不少因素的一系列复杂计算。Core Animation 在你的代理执行任何手动布局更新以前应用自动调整尺寸行为,因此,你能够在须要的时候使用代理去微调自动调整尺寸布局的结果。

手动布局你的图层层次结构

在 iOS 和 OS X 上,你能够经过实现父图层的代理对象的 layoutSublayersOfLayer: 方法来手动处理布局。你可使用该方法调整图层中任意子图层的尺寸和位置。当进行手动布局更新时,由你来执行必要的计算来定位每一个子层

若是你实现一个自定义的图层子类,你的子类能够重写 layoutSublayers 方法,使用该方法(不是代理)去处理任何布局任务。你应该仅在你想完整的控制你自定义图层类的子图层的位置时,才重写此方法。替换默认的实现会阻止 Core Animation 在 OS X 上应用约束或自动调整尺寸规则。

子图层和剪切

不想视图,父图层不会自动剪切子图层的内容,这会致使子图层内容会延伸到父图层外面。换言之,父图层默认容许子图层完整的展现它的内容。然而,你能够经过将图层的 masksToBounds 属性设置为 YES 来将剪切功能生效,

图层的剪切蒙版的形状包含图层的圆角,若是指定圆角的话。图 4-3 展现了图层 masksToBounds 属性如何影响图层圆角。当此属性设置为 NO 时,子类会完整的显示其内容,即便它们内容超出了父图层的边界。将此属性的值改成 YES 会致使它们的内容被剪切。

图 4-3 剪切超出父图层边界的子图层的内容

转换图层之间的坐标值

有时,你可能须要将一个图层的坐标值转换到同屏幕上不一样图层上的坐标值。CALayer 类提供一系列简单的转换方法来达到你的目的:

  • convertPoint:fromLayer:
  • convertPoint:toLayer:
  • convertRect:fromLayer:
  • convertRect:toLayer:

除了转换点和矩形值以外,你也能够经过 convertTime:fromLayer:convertTime:toLayer: 方法来转换两个图层之间的时间值。每一个图层都定义本身的时空,使用本身的时空去同步动画的开始和结束时间和系统的其他部分。时空默认是同步的;可是,若是你改变一系列图层的动画速度,这些图层的时空也会相应改变。你可使用时间转换方法来考虑任何此类因素,来确保两层的时间同步。

最后

相关文章
相关标签/搜索