Core Animation编程指导(二)-Core Animation基础知识

CoreAnimation提供一个用来动画展示视图和其它视觉元素的通用系统. CoreAnimation并不能代替APP中的view, 相反, 它是一种与view结合使用的技术, 用来提供更好的性能和动画支持. 它通过将view的内容缓存在bitmap(bitmap可以直接被硬件操控)中来实现这中特性的. 在某些情况下, 这种缓存行为可能需要你重新考虑如何呈现和管理应用程序的内容, 但是大多数时候你使用CoreAnimation时, 你并不知道它的存在. 除了缓存视图内容之外, CoreAnimation还定义了一种方法来指定任意视觉内容, 将该内容和view结合, 并将其与其他内容一起动画显示.

使用CoreAnimation动画展示view或其他可是对象的改变. 大多数修改是涉及到视觉对象的属性. 例如, 你可以使用CoreAnimation来对view的position, size,或透明度的改变进行动画. 当你修改这些属性CoreAnimation的动画帧率低于每秒60帧(动画一般是每秒60帧). 相反, 你可以使用CoreAnimation在屏幕上移动view的内容, 淡入淡出该内容, 对view使用变换, 或者改变其他视觉属性.
Core Animation 基础知识

Layer是图形绘制和动画的基础


layer对象是3D空间的二维表示, 它是CoreAnimation能力的核心对象. 类似view, layer管理你的界面的几何, 内容, 以及一些其他视觉相关的特征信息. 和view的区别是, layer没有定义自己的外观, 而是仅仅管理着和bitmap有关的一些状态信息. 这个bitmap可以来源view自己绘制的或者由你设置的Image. 因此, App中你使用的main layer可以视为App的模型对象, 因为他们管理着数据. 记住这个概念很重要, 因为这会影响动画的行为.

基于Layer的绘制模型

绝大多数layer不会进行实际绘制, 相反, 一个layer对象会捕获App中的图形内容, 然后将这些内容保存为bitmap, 有时layer对象也被称为view的后背存储. 之后当你修改layer的某个属性时, 你所做的只是改变和layer相关联的状态信息. 当某次改变产生了动画, CoreAnimation会将layer的bitmap和新的状态信息递交给图形硬件, 这个图形硬件工作是将新状态的bitmap进行渲染, 如图1-1所示. 在硬件中渲染比在通过软件做动画更快.


图1-1CoreAnimation是如何绘制内容的

因为layer是通过操纵静态的bitmap, 这和传统基于view的绘制是有很大的区别的. view的更新一般是通过调用view的drawRect:方法并穿一个参数过去来绘制内容的. 但这是比较高消耗的, 因为这个操作要占用CPU并在主线程中完成的. 而基于layer的绘制是由CoreAnimation将新的bitmap提交图形硬件完成同样的内容更新, 这样可以节省CPU时间, 也不会堵塞主线程.

尽管CoreAnimation会尽量使用缓存内容, 但是App必须提供初始的layer内容, 并且要时不时地更新. 创建带内容的layer方法有多种, 在本系列文章中的第三篇中的为layer提供内容部分会具体讲到

基于Layer的动画

layer的数据和状态信息会和layer在屏幕上的视觉呈现分离开来(解耦). 这个解耦过程让CoreAnimation可以在layer的状态新值和旧值间插入自己和动画. 举个例子, 改变layer的position属性会导致CoreAnimation将layer从当前位置移动到新位置. 类似地, 改变其他属性也产生一个合适的动画. 图1-2展示了几个常见的layer属性动画. 要看全部的layer属性动画, 请看后面系列章节-补充中的Animatable属性

图1-2 layer动画的几个列子

在动画的过程中, 动画的是有CoreAnimation在硬件上一帧一帧的画出来的. 你只需要设置动画的起始和结尾点就可以了. 你可以设置自定的timing信息或者其他动画属性, 如果你不设置这些值, CoreAnimation会自动帮你设置默认值.

关于如何创建animation对象和设置animation参数, 请看本系列文章四- Core Animation编程指导(四)-对Layer的content做动画

layer对象定义了自己的几何表示

layer的工作之一就是管理内容几何表示. 可见的几何图形包含了内容的位置, 边界(bounds), 以及layer是否旋转, 是否缩放, 是否变换过这些信息. 和view一样, 一个layer对象使用frame和bounds这两个属性来定位layer和layer内中的内容. layer拥有view没有的属性, 比如anchor point, 该属性定义了操作发生的点. layer的某些几何学上的设置也和view中的设置不同.

layer使用两种类型的坐标系

layer使用基于点的坐标系和单元坐标系(unit coordinate system)来确定内容的位置. 使用哪个坐标系取决于layer所传达的消息类型. 基于点坐标系通常用来设置的值和其他layer相关或直接映射到屏幕上的坐标系, 比如layer的position属性. 单元坐标系用来设置的值和屏幕位置无关而是和其他值有关, 比如layer的anchorPoint属性, 该属性的值和屏幕无关而是和layer自身的bounds有关, 因为bounds会变化.

通常在定位layer的位置和大小时, 使用点坐标系. 像layer的属性boundsposition. bounds属性定义了layer自己的坐标系和layer在屏幕上的大小. position确定了layer在父坐标系中的位置. 尽管layer也有属性frame属性, 但是不怎么常用, 因为frame是一个计算属性, 是通过positionbounds计算而来.

layer的boundsframe属性都是基于iOS的默认坐标系, iOS中的默认坐标系的远点在左上方, 是ULO型, 而OSX中的坐标系是LLO型, 所以在两个平台共用一份CoreAnimation代码时要注意这之间的区别.

图1-3 iOS和OSX中layer的几何表示

注意上图中的position属性是指layer的中心点, 这个属性是layer属性中受anchorPoint影响的几个属性之一. 锚点(anchorPoint)表示某个点在坐标系中的起始位置.

锚点是少数几个使用单元坐标系的属性. CoreAnimation使用单元坐标系来表示因layer的大小改变而改变的属性. 单元坐标系的单位是百分比, 范围是0.0-1.0. 比如, 沿x轴, 左边起点是0.0, 右边结束是1.0, 沿y轴的话, 要看你使用的平台, 如图1-4.


图1-4 iOS和OSX中的默认单元坐标系

注意:在OS X 10.8之前geometryFlipped属性是用来修改y轴的方向的. 当涉及flip transform时, 某些时候你可以使用该属性来纠正layer的方向. 比如, 如果父view使用了flip transform, 其子view(以及对应的layer)中的内容也会被反转. 在这种情况下, 将子layer的geometryFlipped属性设置为YES可以解决这个问题. 在OS X 10.8及以后版本, APPKit会为你管理这个属性, 所以你不需要关系. 对于iOS中的APP来说, 你可以完全忽略它.

不管使用哪个坐标系, 坐标值都是浮点类型的. 使用浮点值的目的是为了定位精确, 浮点数的使用允许你指定可能位于整数坐标值之间的精确值. 使用浮点数是比较方便的, 特别是在retina显示屏(一个点中包含多个像素点)上进行绘制时, 使用浮点数让你不用考虑设备屏幕的分辨率, 你只需要关注你想要的精度即可.

锚点对几何操作的影响

对layer的几何操作都是基于锚点的, 特别是当你在对layer做positiontransform的更新时, 锚点对这影响很大. 图1-5展示了改变锚点对position的影响. 即使layer没有移动layer, 但是你通过将锚点从layer的中心移到bounds的origin位置, layer的position属性会自动改变.

图1-5 layer的锚点是如何layer的position的

图1-6展示了改变layer的锚点是如何影响layer的transform的. 当你对layer加上一个旋转变换时, 该旋转是以锚点为中心进行的. layer的锚点默认设置在layer的中心点. 所以正常的加上旋转不会有啥大问题的, 但是你将锚点改变后, 旋转的结果可能就是不是想要的.

image

layer能在进行三维操作

每个layer都有两个transform矩阵, 你可以使用这两个矩阵来控制layer和layer中的内容. 类CALayer的属性transform可以控制layer和layer中的内容. 通常使用transform属性来控制layer本身, 比如缩放或旋转或临时更改其位置. 而属性sublayerTransform是用来加在layer中的子layer, 并且通常用于向场景的内容添加透视视觉效果.

transform(变换)是通过将坐标点乘以一个矩阵来得到一个新的坐标点的实现的. 因为CoreAnimation中的坐标是三维的, 所以每个坐标点有四个值. 变换时需要使用一个4x4的矩阵, 如图1-7. 在CoreAnimation中使用类型为CATransform3D的结构体来表示图中的transform. 幸运的是, CoreAnimation已经帮你定义了好一大堆用于操纵CATransform3D结构体的函数, 比如创建一个scale(缩放), translation(位移), rotation(旋转)类型的矩阵, 还有可以用于比较两个transform矩阵的函数. 另外, CoreAnimation扩展了KVC来支持让你通过keyPath来修改transform, 对于你可以修改的keyPath列表, 你可以查看补充中的CATransform3D Key Paths

图1-7 使用矩阵来转换坐标

图1-8显示了一些更常见的转换的矩阵设置. 通过任意坐标乘identical transform矩阵会返回完全相同的坐标。对于其他转换,如何修改坐标完全取决于您更改哪些矩阵中的组件。例如,为了只沿x轴进行转换,需要为转换矩阵的tx组件提供一个非零值,并将ty和tz值保留为0。对于旋转,你需要提供目标旋转角适当的正弦和余弦值的。


图1-8 常用transform的数学矩阵构造

layer的层级树能够反映不同方面的动画状态


app在使用CoreAnimation的时候, 会拥有三种类型的layer. 每种layer在APP显示内容的过程中起到不同的作用:

  • model layer tree(简称layer tree)中的layer, 这个对象用来存储动画target值的model对象. 当你修改layer的属性时, 改变的就是个这个layer
  • presentation tree中的layer, 该树包含的是运行中动画的动态数据. 鉴于layer tree中包含的是动画的target信息, 所以presentation tree中的layer反映了当前屏幕上显示的值. 所以不要去修改presentation中的layer对象, 但是你可以获取该树中的layer对象保存的当前动画信息来创建一个新的动画.
  • render tree中的layer是CoreAnimation私有的, 实际用来展示动画的.

上面三组layer和组织结构和view类似. 事实上, 对于一个为所有view开启layer的APP来说, 每个layer树的初始结构和相关联的view结构一致. 但APP可以往初始化树中添加额外的layer(和view无关连的), 这样做, 你或许可以提供性能, 因为如果只是单独显示内容的话, 无需使用view, 使用layer就行了. 图1-9显示的是一个demo中的layer分解图. 在该demo中, window包含一个content视图, 视图中又有一个button和两个独立的layer对象. 每个view都有一个与之相对应的layer对象, 该layer对象是layer树的组成部分.


图1-9 layer

layer tree中的每个对象在presentation tree和render tree中存在与之相对应的对象, 如图1-10所示. 正如前面提到过, App的主要工作集中在layer tree, 偶尔会访问presentation tree中的对象. 特别地, 通过访问layer tree中对象的presentationLayer属性能够得到presentation tree中相对应的对象. 这样你可以通过presentation tree中对象来获取动画过程中layer的属性信息.

图1-10 layer tree

重要: 仅当动画运行时, 你才能访问presentation tree中的layer对象. 动画过程中, presentation tree中layer值为当前那一刻的. 然而layer tree中的layer反应的最终状态, 等于你代码中animation的final state.

layer和view之间的关系


layer不能完全替代view, 也就是说, 你的App界面不能完全有layer来搭建. layer是view的基础, 特别当需要为view添加高频动画或者绘制一些内容时. 但layer也有自身的局限性, 比如, layer不能处理用户事件, 所以无法和用户交互. 所以APP中至少需要一个或更多的view来处理交互.

在iOS中, 每个view的背后都有个layer对象, 但在OS X中你必须自己决定那个view需要layer. 在OS X v10.8和之后的版本中, 往所有的view中添加layer是可取的, 但你也可以不这样做, 也可以将某个view后的layer disable掉. layer确实增加了应用程序的内存开销,但它们的好处往往大于缺点,所以在禁用layer支持之前最好测试应用程序的性能。

layer-backed view是指一个view背后有一个layer支持, 在layer-backed视图中, 系统会创建底层的layer并保持和view同步. 所有的iOS视图都是layer-backed, 而且大多数OS X也是这样的. 但是, 在OS X中, 你还可以创建layer-hosting view-由你自己为view提供layer对象. 对于layer-hosting视图, APPKit采用的是一种不负责的管理方法来管理layer, 也不会负责layer和视图的同步, 也就是说, view改变了, 不回去相应的修改layer.

注意:对于layer-backed视图, 建议你尽可能地操作view, 而不是layer. 在iOS中, 视图只是一个对layer对象的简单包装, 所以你对layer所做的任何操作不会有啥问题. 但是, 在iOS和OSX中都存在这样的情况, 直接操作layer而不是view会产生不可预期的后果, 后续章节, 会尽力指出这些陷阱.

除了与视图相关的layer之外,还可以创建没有对应视图的layer对象。您可以将这些独立的layer对象嵌入应用程序中任何其他layer对象的内部,包括与视图关联的那些layer对象。您通常使用独立的layer对象作为特定优化路径的一部分。例如,如果您想在多个地方使用相同的图像,则可以加载该图像一次,并将其与多个独立layer对象相关联,并将这些对象添加到layer树中。然后,每个layer都引用源图像,而不是试图在内存中创建该图像的自己的副本。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 1 CALayer IOS SDK详解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi阅读 4,987评论 3 23
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 最快让你上手ReactiveCocoa之进阶篇 8 次尝试,带你走进 iOS 精益编程 招聘一个靠谱的iOS 64...
    Hades_L阅读 215评论 0 2
  • 求学是获得势能的重要途径,具备高势能的学校和高势能的导师对于提高学生的势能非常重要。巴菲特的成就很大程度有赖于格雷...
    微澜09阅读 300评论 0 0