Core Animation Programming Guide

Building a Layer Hierarchy

大多数时侯,使用 layer 的最好方式就是同视图对象连着一块用。但是有时候需要增加额外的 layer 对象提升视图架构。当提升性能或者单独在仕途上实现一个特性比较困难的时候你可以尝试使用。在使用时,你需要了解怎么管理你创建的 layer 层次。


重点:在 OS X v10.8 及以后的系统中,推荐你就可能的使用 layer-backed 视图。OS X 版本中的 layer 重绘策略可以自定义 layer-backed 视图行为,而且也可以得到你单独使用 layer 追求的性能。



Arranging Layers into a Layer Hierarchy

layer 层次在很多地方同视图层次很相似,你把一个 layer 嵌入到另一个中,相当于在这两个 layer 之间创建了一个父子关系。这个父子关系会在很多方面影响到子 layer。例如,它的内容坐落在父层内容之上,它的位置坐标在父层的坐标系统中指定,而且它还会受到父层变形应用的影响。

Adding, Inserting, and Removing Sublayers

每一个 layer 对象都有添加、插入和移除子层的方法。表 4-1 总结了这些方法和他们的行为。

表 4-1 Methods for modifying the layer hierarchy

Behavior Methods Description
Adding layers addSublayer: 添加一个子层 layer 对象到当前 layer。子层 layer 被添加到 layer 的子层列表的最后。这会导致在所有子层 layer 具有相同的 zPosition 值时新添加的被显示在最上面。
Inserting Layers insertSublayer:above:
insertSublayer:atIndex:
insertSublayer:below:
把 layer 插入到子层列表中的某个索引位置或相关的子层位置。当插入到另一个子 layer 上面或者下面时,只需要指出子 layer 的索引位置。实际的可见取决于他们的 zPosition 属性值以及它们在子层列表中的位置。
Removing Layers removeFromSuperlayer 从父层中移除子 layer。
Exchanging Layers replaceSublayer:with: 用一个 layer 替换另一个子 layer。如果你将要插入的 layer 已经在另一个 layer 层次中,会从这个层次中移除。

当使用自己创建的 layer 对象时可以使用以上的方法。但是不能在 layer-backed 视图中使用它们。对于单独创建的 layer,一个 layer-backed 视图相当于一个父层。

Positioning and Sizing Sublayers

当添加或者插入子层 layer 时,在它们显示在屏幕上之前你必须设定尺寸和位置。你可以在把它们加入到 layer 层次后修改尺寸和位置,但是你要养成创建 layer 时设置这两个值的习惯。

通过 bounds 和 position 属性设置子层 layer 的尺寸和它在父层 layer 中的位置。bounds 的原点总是 (0, 0),尺寸可以任意设置。position 属性值可以理解为是 layer 的锚点,默认是 layer 的中心。如果你不为这两个属性赋值, Core Animation 会做如下的设置:

myLayer.bounds = CGRectMake(0, 0, 100, 100);

myLayer.position = CGPointMake(200, 200);

重点: layer 的宽和高总是使用整数。


How Layer Hierarchies Affect Animations

一些父层 layer 属性可以影响到它的子 layer 动画效果。speed 属性就是这样的一个属性,这个属性的默认值是 1.0,修改为 2.0 的话会导致动画运行速度快两倍。这个属性不但会影响到属性的对象,还会影响到这个 layer 对象的子层中,改变也是多次的。如果父子层的 layer 都有一个 2.0 的速度,那么子层中的动画速度将会是原始速度的四倍。

大多数的 layer 改变影响到子 layer 都是可以预测的。例如,在 layer 上应用一个旋转变形,会应用到它的子层中。同样,改变 layer 的不透明度也会改变它的子层。layer 尺寸的改变遵守布局规则,详见 Adjusting the Layout of Your Layer Hierarchies.

Adjusting the Layout of Your Layer Hierarchies

Core Animation 支持子层响应父层尺寸和位置改变的集中适应选项。在 iOS 中,layer-backed 视图的普遍使用使得创建 layer 层次不那么重要;只有手动布局的更新才支持。对于 OS X,几个可用的选项使得管理 layer 层次更容易些。

Layer-level 布局仅仅在你使用单独的 layer 对象创建 layer 层次时才相关。如果你的 app 的 layer 全部是何视图相关的,使用 view-based 布局来支持尺寸和位置的更新响应变更。

Using Constraints to Manage Your Layer Hierarchies in OS X

限制让你使用 layer 和 它父层或兄弟层之间的详细关系集合来指明尺寸和位置。定义限制条件需要以下步骤:

  1. 创建一个或多个 CAConstraint 对象。使用这些对象定义限制条件的参数。
  2. 把限制条件对象添加到你想修改的 layer 的属性中。
  3. 检索共享的 CAConstraintLayoutManager 对象,赋给即时的父层 layer。

图 4-1 展示了你可以用来定义一个条件限制以及它们能影响的 layer 方面的属性。你可以限制条件改变 基于其他 layer 的位置和边界的layer 的位置。你也使用它们改变 layer 的尺寸大小。这些改变可以是基于父层或者相关的另一个 layer 的比例。你甚至可以为改变结果添加一个缩放因子或者常量。这个额外的灵活性使得用一个简单的规则集控制 layer 的尺寸和位置非常合适。

图 4-1 Constraint layout manager attributes

每一个限制条件对象都包括着一个两个 layer 沿着同一个坐标轴的几何关系。两个条件限制对象中的最大的可能被赋值到每一个坐标轴,而且它是这两个条件限制决定哪个属性是可改变的。例如,如果你为 layer 指定了左右边距限制,layer 的尺寸就会变化。如果你为 layer 指定了左边距和宽度, layer 右边距的位置会变化。如果你只知名了 layer 的一个边距限制条件,Core Animation 创建一个隐式的限制条件来确保在给定维度内的 layer 的尺寸。

当创建约束条件时,你需要指出三个信息:

  • 你想约束的 layer 部分。
  • 作为引用使用的 layer。
  • 用在比较上的应用 layer 部分。

代码 4-1 简单地展示了把一个垂直的中点 layer 钉在父层 layer 的垂直中点上。当引用自父层 layer 时,使用字符串 superlayer 。这是一个为引用自父层 layer 预留的特殊字符串名称。在使用的时候就不需要一个指向 layer 的指针或者知道 layer 的名字。它也允许你改变父层 layer,在新的父层 layer 中自动拥有约束的应用。(当创建相对于兄弟层的 layer 约束时,你必须使用 name 属性明确兄弟层 layer。)

代码 4-1 Defining a simple constraint

[myLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]];

为了在运行时应用约束条件,你需要把 CAConstraintLayoutManager 对象归属到即时的父层 layer。每一个 layer 负责管理它的子层 layer 的布局。把布局管理赋给父层相当于告诉 Core Animation 应用被子层定义的约束条件。布局管理对象自动应用约束条件。赋给父层 layer 后,你不必告诉父层更新布局。

通过图 4-2 可以观察到更详细的约束条件如何工作的内容。在例子中,这个设计需要 layerA 剩余为改变的宽度和高度,和 layerA 在它的父层 layer 剩余的中心。除此之外,layerB 的宽度必须和 layerA 的相匹配,layerB 的顶部边缘必须在 layerA 的下边缘保留 10 points,而且 layerB 的底部边缘必须在父层 layer 的底部边缘保留 10 points。代码 4-2 展示了怎么为这个例子创建子层 layer 以及约束条件。

图 4-2 Example constraints based layout

代码 4-2 Setting up constraints for your layers

// Create and set a constraint layout manager for the parent layer.

theLayer.layoutManager = [CAConstraintLayoutManager layoutManager];


// Create the first sublayer.

CALayer *layerA = [CALayer layer];

layerA.name = @"layerA";

layerA.bounds = CGRectMake(0.0, 0.0, 100.0, 25.0);

layerA.borderWidth = 2.0;

// Keep layerA centerd by pinning its midpoint to its parent's midpoint.

[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]];

[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]];

[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]];

[theLayer addSublayer:layerA];


// Create the second sublayer

CALayer *layerB = [CALayer layer];

layerB.name = @"layerB";

layerB.borderWidth = 2.0;


// Make the width of layerB match the width of layerA.

[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth relativeTo:@"layerA" attribute:kCAConstraintWidth]];


// Make the horizontal midpoint of layerB match that of layerA

[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"layerA" attribute:kCAConstraintMidX]];


// Position the top edge of layerB 10 points from the bottom edge of layerA.

[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"layerA" attribute:kCAConstraintMinY offset:-10.0]];


// Position the bottom dedge of layerB 10 points
// from the bottom edge of the parent layer.

[layerB addConstrait:[CAConstraint constraintWithAttribute:kCAConstraintMinY relativeTo:@"superlayer" attribute:kCAConstraintMinY offset:+10.0]];

[theLayer addSublayer:layerB];

在代码 4-2 中有一件有趣的事情需要注意,layerB 的尺寸大小在代码中并没有显式地设置。因为约束条件的定义,layerB 的宽度和高度在布局更新时自动设定。因此,就没有必要用边界尺寸设置了。


警告:当创建约束条件时,别在你的约束条件中循环引用。循环约束条件使得需要的的布局信息不能有效的被计算出来。当碰到循环应用时,布局的行为不能被定义。


Setting Up Autoresizing Rules for Your OS X Layer Hierarchies

自动调整规则是 OS X 中另一个调整 layer 尺寸和位置的方式。使用自动调整规则,你设计你的 layer 的边缘是否应当维持在一个固定值上或者相对于父层 layer 边缘的可变距离。你可以简单地设计你的 layer 的宽度和高度是否可变。这个关系总是围绕在 layer 和它的父层 layer 之间。你不可以在 layer 和它的兄弟层之间使用。

为了设置 layer 的自动调整规则,你需要赋给 autoreisizingMask 属性一个合适的常量。默认情况下,layer 被配置成固定的宽度和高度。在布局期间,layer 精确的尺寸和位置被 Core Animation 自动计算,并且调用一系列的基于许多方面的计算。Core Animation 在它调用你的委托代理做任何手动布局更新之前会应用自动调整行为,因此你可以根据需要使用代理微调自动调整布局的结果。

Manually Laying Out Your Layer Hierarchies

在 iOS 和 OS X 中,你可以通过父层 layer 的代理对象实现 layoutSublayersOfLayer: 方法,以便手动处理布局。可以用这个方法调整当前嵌入的子层 layer 的尺寸和位置。当手动更新布局时,取决于你来表现必须的计算以便摆放每一个子层 layer。

如果你正实现一个自定义的 layer 子类型,你的子类可以重写 layoutSublayers 方法,使用此方法(代替委托代理)处理任意的布局任务。你应该只重写这一个方法,以防在你自定义的 layer 类中超出父层 layer 的摆放。在 OS X 上替换默认的实现会阻止 Core Animation 应用约束条件和自动调整规则。

Sublayers and Clipping

不像视图,一个父层 layer 不会自动剪掉地子层 layer 超出它边界的内容。父层 layer 允许它的子层 layer 默认显示他们全部的内容。然而你可以通过设置 layer 的 masksToBounds 属性为 YES 重新开启剪切。

如果被指定,layer 的外形剪切包括 layer 的圆角。图 4-3 展示一个 layer 证明了 masksToBounds 属性是如何用圆角影响 layer 的。当这个属性被设置为 NO 时,子层 layer 会全部显示,即使超出了父层 layer 的边界。改变属性为 YES 会导致他们的内容被剪切。

Converting Coordinate Values Between Layers

偶尔你可能需要从一个 layer 的坐标值转换为同一屏幕下不同 layer 中的位置坐标。CALayer 类提供了一些常规的转换方法:

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

除了转换点和 rectangle 值,你也可以在 layer 之间使用 convertTime:fromLayer: 和 convertTime:toLayer: 方法转换时间值。每一个 layer 定义了它自己的当地时间空间,而且使用此时间空间同步动画的起始。这些时间空间默认被同步;然而,如果你为 layer 集合改变动画时间,这些 layer 的时间空间会相应地改变。你可以使用时间的转换方法确保 layer 之间的时间是同步的。

原文地址:https://www.cnblogs.com/1oo1/p/4253617.html