0%

CoreAnimation编程指南翻译(二):Core Animation基础

Core Animation用于为视图或其他可视化元素添加动画. Core Animation会将视图内容缓存到一个能直接被图形硬件使用的位图中. 在某些特定场景下, 这种缓存行为可能会影响应用内容的呈现形式和组织方式. 但在大部分情况下, 开发者都是在无感知的使用Core Animation. 除了缓存视图内容之外, Core Animation还可以将任意可视化内容与视图内容结合在一起进行动画.

大部分动画都与可视化对象的属性值改变有关, 例如视图的位置,大小或透明度变化. 当这些属性值变化时, Core Animation会根据属性的类型以及旧值和新值自动执行动画.

图层是绘图和动画的基础

图层是Core Animation中一切操作的核心, 表示3D空间中的一个2D平面. 图层管理平面的几何, 内容以及视觉属性信息. 与视图不同的是, 图层并不会定义自身的外观样式, 而只是管理一些位图状态相关的信息. 位图可以由通过绘制生成, 也可以直接设置为特定的图像. 由于图层只用于数据管理, 因此,可以将图层视为一个模型对象. 这个概念十分重要, 这将影响到动画的行为.

基于图层的绘图模型

应用中的大部分图层都不会进行实际的绘制工作. 相反, 图层会捕获应用所提供的内容, 并将其缓存到一个位图中, 这个缓存也被称为后备存储(backing store). 当图层属性被修改时, 实际上被修改的只是与图层对象相关联的状态信息. 当属性修改触发动画时, Core Animation会将图层的位图和状态信息传递给图形硬件, 图形硬件会使用新的状态信息渲染位图(Figure 1-1). 在硬件中操作位图比在软件中更快的生成动画.

  • Figure 1-1 Core Animation如何对内容进行绘制
    Core Animation如何对内容进行绘制

由于Core Animation直接对位图进行操作, 因此基于图层的绘图与传统的基于视图的绘图有着显著区别. 在基于视图的绘图中, 对视图本身的改变会导致视图的drawRect:方法被调用, drawRect:方法会使用新的参数进行重绘, 重绘的代价十分昂贵, 因为绘制是在主线程中通过CPU完成的. 而Core Animation则通过硬件中的位图操作来实现相同或类似的效果, 避免了这些额外开销.

即便Core Animation会尽可能的使用内容缓存, 但应用仍
必须提供一个初始内容并进行实时更新. 有多种方法可以为图层提供内容, 详情可参阅: 为图层提供内容

基于图层的动画

图层的数据和状态信息与其在屏幕上的可视化呈现是分离的. 这种分离式设计使得Core Animation支持插值运算, 将旧值变化到新值的过程形成动画. 例如, 如果一个图层的position属性发生变化, Core Animation会将图层从当前位置移动到指定位置, 并执行一段位移动画. 其他类似属性的更改也会带有相应的动画. Figure 1-2中说明了一些可以在图层上执行的动画类型. 更多可以触发动画的属性列表参阅:可动画的属性列表

  • Figure 1-2 图层动画示例
    图层动画示例

在动画过程中, Core Animation已经在硬件中完成了动画的逐帧绘制. 开发者只需要指定动画的起点和终点, 其他工作都由Core Animation完成. 另外, 开发者还可以自定义动画计时相关的信息, 以及其他的一些动画参数, 否则, Core Animation将使用默认参数实现动画.

更多关于如何初始化动画和配置动画参数的资料可以参阅: 图层动画

图层几何的定义

图层的一个功能是管理其内容在几何上的视觉呈现. 视觉几何包含内容的边界, 内容在屏幕上的位置以及图层的旋转,缩放或变换等信息. 跟视图一样, 图层也有frame和bounds属性, 用于定位图层和及其内容. 图层还具有一些视图没有的属性, 比如锚点(anchor point), 锚点定义了操作在何处产生作用. 设置图层几何属性的方式也与视图有所区别.

图层使用两种类型的坐标系

图层同时使用点坐标系和单位坐标系. 使用哪种坐标系由具体所需要传达的信息决定. 当需要把属性值映射为屏幕坐标或其他图层的相应值时使用点坐标系, 比如图层的position属性. 当属性值不与屏幕坐标相关联时使用单位坐标系, 比如图层的anchorPoint属性.

点坐标系最常见的使用情形是使用boundsposition属性来指定一个图层的大小和位置. bounds属性定义了图层自身的坐标系, 并指定了图层在屏幕上的大小. position属性定义了图层在其父图层坐标系中的位置. 尽管图层还包含一个frame属性, 但frame属性其实是由boundsposition属性计算得出的, 使用的较少.

boundsframe的矩形方向与系统有关. Figure 1-3展示了在iOS和OS X中的系统默认矩形方向. 在iOS中, 矩形的原点默认在左上角, 在OS X中, 矩形的原点默认在左下角. 在编写跨平台代码时需要考虑这个差异.

  • Figure 1-3 iOS和OS X上的图层几何

Figure 1-3中需要注意的一点是position位于图层的中心. 有一些图层属性会随anchorPoint属性的变化而发生改变, position属性便是其中之一. anchorPoint是另外一些坐标的起始点, 更多相关信息请参阅锚点对几何操作的影响.

anchorPoint使用单位坐标系. Core Animation使用单位坐标系来定义在图层大小更改时其值可能会更改的属性. 可以将单位坐标系视为所有可能值的百分比形式, 取值范围由0.0到1.0. 比如, x轴方向上, 左边界代表0.0, 右边界代表1.0, y轴方向上, 单位坐标的取值方向与平台有关, 如Figure 1-4所示.

  • Figure 1-4 iOS和OS X上的单位坐标系

注意: 在OS X 10.8之前, geometryFlipped属性用于修改图层y轴的默认方向. 在OS X 10.8之后, 该属性由AppKit进行管理, 不建议进行手动修改. 对iOS应用来说, 不建议使用该属性.

无论是点坐标系还是单位坐标系的坐标值都使用浮点数. 使用浮点数可以精确表示坐标系取值区间之内的任意值. 浮点数的使用十分方便, 尤其是在打印或在Retina屏幕上进行绘制时, 此时一个点可能由多个像素表示. 使用浮点数可以忽略底层设备的分辨率.

锚点对几何操作的影响

对图层进行几何操作都不可避免的受到锚点的影响. 尤其是操作图层的positiontransform属性时表现的最为明显.

Figure 1-5 说明了锚点对position属性的影响. 即使在图层的位置没有发生变化时, 锚点的变化也会导致position属性值的变化.

  • Figure 1-5 锚点对position属性的影响

锚点对position属性的影响

Figure 1-6 说明了锚点对transform的影响. 当对图层进行旋转变换时, 由于锚点默认在在图层中心, 所以图层会围绕中心点旋转. 但如果改变锚点点位置, 就会产生不同的旋转行为.

  • Figure 1-6 锚点对transform属性的影响

锚点对transform的影响

图层的三维操作

每个图层都拥有两个变换矩阵, 开发者可以通过操作变换矩阵来控制图层. CALayertransform属性可以将变换应用到当前图层以及当前图层的子图层. 通常来说, transform属性只会在对图层进行缩放,旋转或位置变化等操作时才会使用. sublayerTransform属性定义了仅应用在子图层上的变换, 最常见的使用情形是给场景内容添加视觉透视效果.

将初始坐标值与一个矩阵相乘得出的结果就是变换之后的坐标点. 在Core Animation中支持三维赋值, 所以每个坐标点都包含四个值(齐次坐标), 只能与4 * 4的矩阵相乘, 如Figure 1-7所示. 在Core Animation中, 使用CATransform3D类型表示图1-7中的变换. 幸运的是, 开发者不必直接操作这个结构体, Core Animation提供了一套完整的方法用来创建缩放, 平移和旋转矩阵以及进行矩阵比较. 此外, Core Animation还支持KVC, 支持KVC的属性列表参阅: CATransform3D Key Paths.

  • Figure 1-7 坐标的矩阵变换

坐标的矩阵变换

Figure 1-8中展示了一些常用变换所用的矩阵. 任何坐标与图中的identity矩阵相乘会得到与自身相同的坐标. 对于其他变换而言, 结果坐标点取决于矩阵的设置. 比如, 想要沿x轴进行平移, 只需要将translation矩阵中的tx设为非零值, ty和tz设为0. 对于旋转变换, 只需要提供目标角度对正弦和余弦值.

  • Figure 1-8 常用变换矩阵

常用变换矩阵

图层树对动画的影响

Core Animation包含三种类型的图层对象. 在将应用内容渲染到屏幕上的过程中, 不同类型的图层对象分别扮演了不同的角色:

  1. 模型图层树(或简称”图层树”,model layer tree)中的对象在大部分情况下负责直接与应用交互. 模型图层对象中存储了所有动画的目标值. 只要涉及到图层的属性修改, 就会使用到模型图层对象.
  2. 呈现树(presentation tree)中的对象包含了当前正在执行的动画当前时刻的瞬时值. 任何情况下都不应该修改呈现树中的对象.
  3. 渲染树(render tree)中的对象都是Core Animation的私有对象, 代表实际要执行的动画.

与视图结构类似, 每种类型的图层对象都处于分层结构之中. 实际上, 对使用图层的视图应用而言, 初始状态的图层树结构与视图结构树完全匹配的. 但是, 应用可以需要创建不与视图相绑定的图层(独立图层), 并将其添加到图层树中. 合理的使用独立图层可以优化应用程序的性能. Figure 1-9 中是一个简单的iOS应用中的图层分解.

  • Figure 1-9 图层分解

图层分解

对于图层树中的每个对象而言, 在呈现树和渲染树中都存在与之相匹配的对象, 如Figure 1-10所示. 如之前所描述的那样, 应用主要使用图层树中的对象, 但有时也会访问呈现树中的对象. 访问图层对象的presentationLayer属性会返回呈现树中与之相匹配的对象, 可以在动画执行到某个时刻时通过该属性读取到动画的瞬时值.

  • Figure 1-10 window的图层树

window的图层树

重要说明: 只有在动画执行过程中才能访问呈现树中的对象. 而图层树中永远只会返回代码设置的动画最终值, 即动画结束状态时的值.

视图和图层的关系

图层并不能替代视图, 也就是说, 不能创建一个只使用图层的应用程序. 图层为视图提供底层基础. 具体而言, 图层使得高帧率的绘制和动画变得更加简单. 但图层不参与事件处理, 内容绘制, 响应链以及其他一些工作. 所以一个应用必须包含一个或多个视图用来处理这些类型的交互.

在iOS应用中, 每个视图都绑定有一个相应的图层对象. 但在OS X中, 由开发者指定哪些视图需要绑定图层. 在OS X 10.8或更高版本中, 建议为所有视图绑定图层, 但仍不是必须的. 图层会增大应用的内存开销, 不过使用图层还是利大于弊. 所以在禁用图层之前最好先对应用进行性能测试.

一个绑定了图层的视图被称为图层支持视图(layer-backed view). 对图层支持视图来说, 由系统负责创建底层的图层对象, 并保持图层与视图之间的同步. 在OS X中, 还可以创建图层托管视图(layer-hosting view), 可以由开发者创建图层对象, 然后提供给视图. 对于图层托管视图, AppKit不用对图层进行管理,也不会处理视图同步.

注意: 对图层支持视图来说, 尽量选择操作视图, 而不是图层. 在iOS中, 视图只是图层的一个简单封装, 所以可以对图层的操作有时也能正常生效. 但是在iOS和OS X中都可能存在操作图层后得不到预期结果的情况. 本文档将指出这些陷阱, 并提出解决方案.

除了创建与视图相关联的图层之外, 还可以创建独立图层. 独立图层可以嵌入到任何其他的图层对象中. 合理使用独立图层可以优化应用性能. 比如要在多个地方显示相同的图像, 可以只加载一次图像, 然后将图像与多个独立图层关联, 然后将这些独立图层加入到图层树中进行展示. 这样每个独立图层都只是引用源图像, 而不是为图像创建多个内存拷贝.

有关如何为应用程序视图启用图层支持的信息,请参阅为应用提供Core Animation支持. 有关如何创建及使用图层树的信息, 请参阅: 创建图层树