圖層的樹狀結構 >巨妖有圖層,洋蔥也有圖層,你懂嗎?我們都有圖層 -- 史萊克 Core Animation其實是一個令人誤解的命名。你可能認為它只是用來做動畫的,但實際上它是從一個叫做*Layer Kit*這麼一個不怎麼和動畫有關的名字演變而來,所以做動畫這隻是Core Animation特性的冰 ...
圖層的樹狀結構
>巨妖有圖層,洋蔥也有圖層,你懂嗎?我們都有圖層 -- 史萊克
Core Animation其實是一個令人誤解的命名。你可能認為它只是用來做動畫的,但實際上它是從一個叫做*Layer Kit*這麼一個不怎麼和動畫有關的名字演變而來,所以做動畫這隻是Core Animation特性的冰山一角。
Core Animation是一個*複合引擎*,它的職責就是儘可能快地組合屏幕上不同的可視內容,這個內容是被分解成獨立的*圖層*,存儲在一個叫做*圖層樹*的體系之中。於是這個樹形成了**UIKit**以及在iOS應用程式當中你所能在屏幕上看見的一切的基礎。
在我們討論動畫之前,我們將從圖層樹開始,涉及一下Core Animation的*靜態*組合以及佈局特性。
##圖層和視圖
如果你曾經在iOS或者Mac OS平臺上寫過應用程式,你可能會對*視圖*的概念比較熟悉。一個視圖就是在屏幕上顯示的一個矩形塊(比如圖片,文字或者視頻),它能夠攔截類似於滑鼠點擊或者觸摸手勢等用戶輸入。視圖在層級關係中可以互相嵌套,一個視圖可以管理它的所有子視圖的位置。圖1.1顯示了一種典型的視圖層級關係
圖1.1 一種典型的iOS屏幕(左邊)和形成視圖的層級關係(右邊)
在iOS當中,所有的視圖都從一個叫做`UIVIew`的基類派生而來,`UIView`可以處理觸摸事件,可以支持基於*Core Graphics*繪圖,可以做仿射變換(例如旋轉或者縮放),或者簡單的類似於滑動或者漸變的動畫。
###CALayer
`CALayer`類在概念上和`UIView`類似,同樣也是一些被層級關係樹管理的矩形塊,同樣也可以包含一些內容(像圖片,文本或者背景色),管理子圖層的位置。它們有一些方法和屬性用來做動畫和變換。和`UIView`最大的不同是`CALayer`不處理用戶的交互。
`CALayer`並不清楚具體的*響應鏈*(iOS通過視圖層級關係用來傳送觸摸事件的機制),於是它並不能夠響應事件,即使它提供了一些方法來判斷是否一個觸點在圖層的範圍之內(具體見第三章,“圖層的幾何學”)
###平行的層級關係
每一個`UIview`都有一個`CALayer`實例的圖層屬性,也就是所謂的*backing layer*,視圖的職責就是創建並管理這個圖層,以確保當子視圖在層級關係中添加或者被移除的時候,他們關聯的圖層也同樣對應在層級關係樹當中有相同的操作(見圖1.2)。
圖1.2 圖層的樹狀結構(左邊)以及對應的視圖層級(右邊)
實際上這些背後關聯的圖層才是真正用來在屏幕上顯示和做動畫,`UIView`僅僅是對它的一個封裝,提供了一些iOS類似於處理觸摸的具體功能,以及Core Animation底層方法的高級介面。
但是為什麼iOS要基於`UIView`和`CALayer`提供兩個平行的層級關係呢?為什麼不用一個簡單的層級來處理所有事情呢?原因在於要做職責分離,這樣也能避免很多重覆代碼。在iOS和Mac OS兩個平臺上,事件和用戶交互有很多地方的不同,基於多點觸控的用戶界面和基於滑鼠鍵盤有著本質的區別,這就是為什麼iOS有UIKit和`UIView`,但是Mac OS有AppKit和`NSView`的原因。他們功能上很相似,但是在實現上有著顯著的區別。
繪圖,佈局和動畫,相比之下就是類似Mac筆記本和桌面系列一樣應用於iPhone和iPad觸屏的概念。把這種功能的邏輯分開並應用到獨立的Core Animation框架,蘋果就能夠在iOS和Mac OS之間共用代碼,使得對蘋果自己的OS開發團隊和第三方開發者去開發兩個平臺的應用更加便捷。
實際上,這裡並不是兩個層級關係,而是四個,每一個都扮演不同的角色,除了視圖層級和圖層樹之外,還存在*呈現樹*和*渲染樹*,將在第七章“隱式動畫”和第十二章“性能調優”分別討論。
###圖層的能力
如果說`CALayer`是`UIView`內部實現細節,那我們為什麼要全面地瞭解它呢?蘋果當然為我們提供了優美簡潔的`UIView`介面,那麼我們是否就沒必要直接去處理Core Animation的細節了呢?
某種意義上說的確是這樣,對一些簡單的需求來說,我們確實沒必要處理`CALayer`,因為蘋果已經通過`UIView`的高級API間接地使得動畫變得很簡單。
但是這種簡單會不可避免地帶來一些靈活上的缺陷。如果你略微想在底層做一些改變,或者使用一些蘋果沒有在`UIView`上實現的介面功能,這時除了介入Core Animation底層之外別無選擇。
我們已經證實了圖層不能像視圖那樣處理觸摸事件,那麼他能做哪些視圖不能做的呢?這裡有一些`UIView`沒有暴露出來的CALayer的功能:
* 陰影,圓角,帶顏色的邊框
* 3D變換
* 非矩形範圍
* 透明遮罩
* 多級非線性動畫
我們將會在後續章節中探索這些功能,首先我們要關註一下在應用程式當中`CALayer`是怎樣被利用起來的。
##使用圖層
首先我們來創建一個簡單的項目,來操縱一些`layer`的屬性。打開Xcode,使用*Single View Application*模板創建一個工程。
在屏幕中央創建一個小視圖(大約200 X 200的尺寸),當然你可以手工編碼,或者使用Interface Builder(隨你方便)。確保你的視圖控制器要添加一個視圖的屬性以便可以直接訪問它。我們把它稱作`layerView`。
運行項目,應該能在淺灰色屏幕背景中看見一個白色方塊(圖1.3),如果沒看見,可能需要調整一下背景window或者view的顏色
圖1.3 灰色背景上的一個白色`UIView`
這並沒有什麼令人激動的地方,我們來添加一個色塊,在白色方塊中間添加一個小的藍色塊。
我們當然可以簡單地在已經存在的`UIView`上添加一個子視圖(隨意用代碼或者IB),但這不能真正學到任何關於圖層的東西。
於是我們來創建一個`CALayer`,並且把它作為我們視圖相關圖層的子圖層。儘管`UIView`類的介面中暴露了圖層屬性,但是標準的Xcode項目模板並沒有包含Core Animation相關頭文件。所以如果我們不給項目添加合適的庫,是不能夠使用任何圖層相關的方法或者訪問它的屬性。所以首先需要添加QuartzCore框架到Build Phases標簽(圖1.4),然後在vc的.m文件中引入<QuartzCore/QuartzCore.h>庫。
<img src="./1.4.jpeg" alt="圖1.4" title="圖1.4" width="700"/>
圖1.4 把QuartzCore庫添加到項目
之後就可以在代碼中直接引用`CALayer`的屬性和方法。在清單1.1中,我們用創建了一個`CALayer`,設置了它的`backgroundColor`屬性,然後添加到`layerView`背後相關圖層的子圖層(這段代碼的前提是通過IB創建了`layerView`並做好了連接),圖1.5顯示了結果。
清單1.1 給視圖添加一個藍色子圖層
``` objective-c
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;

@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//create sublayer
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[self.layerView.layer addSublayer:blueLayer];
}
@end
```
圖1.5 白色`UIView`內部嵌套的藍色`CALayer`
一個視圖只有一個相關聯的圖層(自動創建),同時它也可以支持添加無數多個子圖層,從清單1.1可以看出,你可以顯示創建一個單獨的圖層,並且把它直接添加到視圖關聯圖層的子圖層。儘管可以這樣添加圖層,但往往我們只是見簡單地處理視圖,他們關聯的圖層並不需要額外地手動添加子圖層。
在Mac OS平臺,10.8版本之前,一個顯著的性能缺陷就是由於用了視圖層級而不是單獨在一個視圖內使用`CALayer`樹狀層級。但是在iOS平臺,使用輕量級的`UIView`類並沒有顯著的性能影響(當然在Mac OS 10.8之後,`NSView`的性能同樣也得到很大程度的提高)。
使用圖層關聯的視圖而不是`CALayer`的好處在於,你能在使用所有`CALayer`底層特性的同時,也可以使用`UIView`的高級API(比如自動排版,佈局和事件處理)。
然而,當滿足以下條件的時候,你可能更需要使用`CALayer`而不是`UIView`
* 開發同時可以在Mac OS上運行的跨平臺應用
* 使用多種`CALayer`的子類(見第六章,“特殊的圖層“),並且不想創建額外的`UIView`去包封裝它們所有
* 做一些對性能特別挑剔的工作,比如對`UIView`一些可忽略不計的操作都會引起顯著的不同(儘管在這種情況下,你可能會想直接使用OpenGL來繪圖)
但是這些例子都很少見,總的來說,處理視圖會比單獨處理圖層更加方便。
##總結
這一章闡述了圖層的樹狀結構,說明瞭如何在iOS中由`UIView`的層級關係形成的一種平行的`CALayer`層級關係,在後面的實驗中,我們創建了自己的`CALayer`,並把它添加到圖層樹中。
在第二章,“圖層關聯的圖片”,我們將要研究一下`CALayer`關聯的圖片,以及Core Animation提供的操作顯示的一些特性。