I.1 图层树

怪物有好多层,洋葱有好多层,你知道吗?我们都有好多。--Shrek

Core Animation是一个有误导性的名字。你也许猜测它的主要目的是用于动画制作,然而事实上动画只是这个框架中的一方面,这个名字最初用于Layer Kit的动画核心的简称。

Core Animation是一个组合引擎,他的主要职责在于在屏幕上尽可能快地将不同的视觉内容组合起来。这些内容被分成独立的层存储在叫图层树的层次结构中。图层树组成了整个UIKit的根基,以及你在一个iOS应用中所有在屏幕上的东西。

在我们开始讨论动画之前,我们将从图层树开始讲解Core Animation的静态构成和布局特征。

图层和视图

如果你先前开发过一个iOS或Mac OS的应用,你可能会对视图的概念比较熟悉。一个视图是一个用于显示内容(如图象、文字或视频等)的矩形对象,并且处理用户鼠标点击或者触摸手势等的输入行为。视图可以构建于另一个视图中来形成一种层次的结构,在这种层次中每个视图控制它子视图的位置。图1.1展示了一个典型的视图层次。

图1.1 一个典型的iOS屏幕(左)和构成它的视图层次(右)

在iOS中,视图都继承自一个共同的基类——UIViewUIView用于处理触摸事件、支持基于Core Graphics的绘制、仿射变形(如旋转或缩放)以及如滑动、渐变等简单动画。

CALayer

CALayer的概念与UIView非常类似。像视图一样,图层也是可以组成层次树结构的矩形对象,并同样包含如图象、文字或背景颜色等内容,控制着子图层的位置。它们有用于实现动画和变形的方法和属性,而UIView的主要特性中用户交互是唯一不归CALayer处理的。

CALayer并无法察觉响应者链(iOS用于在视图层次间传递触摸事件的机制),因此尽管它确实提供方法用于确认是否一个确定的触摸点在某个图层之中(这将在第三章“图层几何学”中进一步讲述),CALayer并不能响应事件。

平行层次

每一个UIView都有一个layer属性,这个属性是一个CALayer的实例。视图负责创建和管理这个layer,并确保当子视图从视图层次结构中增加或移除时,它们背后的图层会在图层树中平行地联系起来(如图1.2所示)。

图1.2 图层树结构(左)反映的相应的视图层次(右)

就是这些背后的图层负责你在屏幕上看见的一切东西的显示和动画。UIView是一个简单的包装用于提供针对iOS的方法,诸如触摸处理以及相应Core Animation的底层方法的高层接口。

为什么iOS有这样两个基于UIViewCALayer的平行层次结构?为什么不是一个结构用于处理一切?原因是用于分离职责来防止重复性的代码。事件和用户接口在iOS和Mac OS上的工作相当不同。一个基于多点触控的用户接口从根本上有别于基于鼠标和键盘的用户接口。这就是为什么iOS有UIKitUIView而Mac OS有AppKitNSView。他们功能相似却在实现上有显著差异。

相比之下绘制、布局以及动画在如iPhone和iPad的触摸屏设备下和笔记本和台式机之中的概念几乎一致。通过分离出这些功能逻辑到独立的Core Animation框架中,Apple可以在iOS和Mac OS中共用这些代码,使得对于Apple自己的操作系统开发小组和第三方开发者们在开发针对这两种平台的应用时有更为相似的体验。

事实上,它们并不是两个,而是四个这样的层次,每一个用于实现不同的功能。除了视图层次和图层树之外还有表示树(presentation tree)和渲染树(render tree)两个。这将分别在第7章“隐式动画”和第12章“速度调整”中进行讲解。

图层功能

那如果CALayer仅仅是UIView的内在工作的实现细节,为什么我们需要深入了解它呢?Apple确实恰当地提供了简单好用的UIView借口,但因此我们就不需要去直接深穷Core Animation本身的细节了吗?

这在某种程度下是正确的,如果出于简单的目的,我们并不需要真的去深究CALayer,因为Apple使其可以通过用简单的高级API来间接作用于动画等的特性中。

但是这种便利性也带来了灵活性的丧失。如果你想做一些有别于常的东西,或者使用一些Apple并没有在UIView的类接口中使用的特性,那你别无选择只能深入到Core Animation之中去调整那些底层一点的选项。

我们说过图层并不能像UIView那样处理触摸事件,那么他们能做什么视图所不能做的?下面列举了一些不能通过UIView而仅能通过CALayer使用的特性:

  • 投射阴影、圆角和有色彩的边框
  • 3D变形和位移
  • 非矩形的边界
  • 内容的透明遮罩
  • 多步非线性动画

我们将在接下来的章节中分别介绍这些特性,但是现在让我们一起看看如何在一个应用中使用CALayer

使用图层

让我们从创建一个用于操作图层属性的简单项目开始。在Xcode中使用Single View Application模板创建一个新的iOS项目。

在屏幕中央创建一个小视图(大概200*200像素)。你可以根据你喜爱的方式,用程序代码或Interface Builder来实现这一切。只要确保你在你的view controller中包含了这一属性来让我们可以直接访问这一视图。我们将之命名为layerView

如果你运行这个项目,应该可以看见一个白色方块在一个浅灰色的背景中(如图1.3)。如果你并没有看到这个情形,你可能需要微调这个窗口或视图的背景颜色。

图1.3 一个白色的UIView在灰色背景中

这看起来并不是非常赞,因此让我们加一点色彩,我们将在这个白色方块里面放一个小的蓝色方块。

我们可以通过简单地使用另一个UIView并把它加为之前创建的视图的子视图来实现这一效果(通过代码或者Interface Builder),但这样并不会教给我们任何关于图层的知识。

因此,让我们创建一个CALayer并把它做为我们白色视图的图层的子图层。尽管layer属性已经在UIView类接口中暴露,但标准的Xcode的iOS项目模板并没有饮食Core Animation的头文件,因此在我们给项目添加相应的框架之前,我们并不能高用任何方法或者访问这处个layer的任何属性。为了实现这一功能,首先添加QuartzCore框架在程序的target's Build Phases中(见图1.4),并且在代码中import QuartzCore在viewController.swift中。[1]

图1.4 添加QuartzCore框架到项目中

接下来,我们可以直接在代码中引用CALayer以及它的属性、方法。在表1.1中,我们程序化创建了一个新的CALayer,设置它的backgroundColor属性,然后将它作为子图层添加到 layerView中的图层中。(这个代码假设我们通过Interface Buiilder创建了这个视图并且早已经链接到layerView出口。)图1.5显示了结果。

CALayer backgroundColor属性是CGColorRef类型的,并不是像UIView类的backgroundColor一样是UIColor类型的,所以当设置颜色时我们需要使用UIColor对象的CGColor属性。如果你喜欢你也可以直接通过使用Core Graphics方法来直接创建一个CGColor,但使用UIColor可以避免当你不再需要使用这个颜色手动释放它的麻烦。

表1.1 向视图添加一个蓝色的子图层

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var layerView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 创建子图层
        let blueLayer = CALayer()
        blueLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0)
        blueLayer.backgroundColor = UIColor.blueColor().CGColor

        // 加入到白色的view中
        self.layerView.layer.addSublayer(blueLayer)
    }
}
图1.5 一个小的蓝色CALayer构架于一个白色的UIView之中

一个视图只有唯一个主图层(自动创建的)但可以拥有无数的附加图层。正如表1.1所展示的,你可以显式地创建独立的图层并把它们作为子图层加入到这个视图的主图层中。尽管可以通过这种方式添加图层,但更多情况下你只需要使用到视图和它们的主图层,并不需要手动创建额外的图层。

在Mac OS 10.8之前,一个显著的缺点就是使用基于图层的视图层次而不是每个视图拥有一个独立的CALayer树结构。但iOS中的轻量级UIView类在使用图层时几乎没有不好的影响。(在Mac OS 10.8中NSView的表现同样有了巨大的进步。)

使用基于图层的视图结构而非由有组织的CALayer的优点是当你仍需要访问所有的底层CALayer特性时,你不会丢失由UIView类提供的高层的API(例如自动调整大小, 自动布局以及事件处理)。

但你可能还是想在一个现实中的应用里使用用有组织的CALayer而非基于图层的视力结构,其原因有:

  • 你可能要写一些同样需要运行于Mac中的跨平台的代码。
  • 你可能要使用许多CALayer的子类(第6章“特定图层”)而且不想创建新的UIView的子类去管理它们。
  • 你可能在做高性能的工作,而任何一点多余的UIView对象会导致不可忽略的差异(尽管通常在这种情况下,你可能会使用例如OpenGL的工具来绘制)。

但这些情况都很少见,通常情况下,基于图层的视图往往更容易使用。

总结

这个章节讲述了图层树——这个平行存在于UIView层次结构下的CALayer对象的层次结构,用于构建iOS接口。我们也创建了我们自己的CALayer并将之添加到图层树中作为我们的实验。

在第2章“主图像”中,我们将讲解CALayer的主图像以及Core Animation提供的操作其如何显示的属性。


  1. 译者使用的是Xcode6.4和Swift,并不需要这一步骤。因为已经import UIKit无需再import QuartzCore

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

推荐阅读更多精彩内容