周末閑來無事, 復習一下Core Animation的相關(guān)知識, 網(wǎng)頁正好跳到Core Animation Basics頁面, 那么就從最基本的概念先入手看看.
Core Animation提供了一個通用的系統(tǒng)來展示動畫. 它并不是app中view的替代物. 相反, 它是一種可以和view協(xié)同工作的技術(shù)以此來提供更好的視覺表現(xiàn)以及對動畫做出更好的支持. 而完成以上工作則是通過將view中的內(nèi)容緩存進位圖中, 然后供圖形設備直接操作. 在一些情況下, 你可能需要思考你要如何展示和管理app中的內(nèi)容, 但是大多數(shù)情況下, 你可以直接使用Core Animation而不需要問其所以然. 除了緩存view的內(nèi)容, Core Animation也可以定義一種方法來指定任意的視覺內(nèi)容, 將內(nèi)容和view整合起來, 最后實現(xiàn)動畫過程.
你使用Core Animation來模擬app中view和視覺對象的變化. 絕大多數(shù)的改變都涉及到修改視覺對象的屬性. 比如, 你可能使用Core Animation來模擬一個view位置, 大小或是透明度的改變. 當你做這樣一個改變的時候, Core Animation就在模擬當前值到新值的變化過程. 你通常不要用Core Animation以1秒60次的速度來替換view中的內(nèi)容, 相反, 你應該使用Core Animation來移動view中的內(nèi)容, 實現(xiàn)淡入淡出的效果, 或是實現(xiàn)圖形的轉(zhuǎn)變以及其他視覺屬性的改變.
Layers為繪圖和動畫提供了基礎(chǔ)
Layer 是Core Animation中的核心, 和view類似, layers也管理著view表層上幾何學, 內(nèi)容和視覺屬性的信息. 但不同于view的是, layers并不定義appearance, 而是僅僅管理著位圖的狀態(tài)信息. 這個位圖可以是view本身, 也可以是你指定的一張固定的圖片. 基于這個原因, 在你app中的主要的layer就被認為是模型對象, 因為它們管理著數(shù)據(jù). 這個概念很重要, 因為它會影響動畫的行為.
基于Layer的繪圖模型
App中的絕大多數(shù)layer并不做實際的繪圖工作. 相反, 一個layer會捕獲app提供的內(nèi)容, 并且將它緩存到一個位圖里, 就相當于是備份下來. 當你之后改變了layer的一個屬性時, 你所做的一切都只是改變跟layer對象相關(guān)聯(lián)的狀態(tài)信息. 當一個改變觸發(fā)了動畫, Core Animation會把layer的位圖和狀態(tài)信息傳遞給圖形硬件, 而圖形硬件做的事情就是用新的信息來渲染位圖. 如下圖所示:

因為這個過程操作的是一張靜態(tài)的位圖, 所以基于layer的繪圖是明顯有別于傳統(tǒng)上基于view的繪圖過程的. 在基于view的繪圖過程中, 對于view的改變總是會調(diào)用view的drawRect:方法, 以此來用新的參數(shù)重繪內(nèi)容. 這個過程的耗費是很大的, 因為整個操作是在主線程, 并且會占用CPU的資源. 而對于Core Animation來講, 其整個繪圖過程都是盡可能在硬件中操作緩存下來的位圖以此來達到繪圖的效果, 兩者在繪圖過程中達到的效果是一樣的, 但是基于layer的繪圖過程明顯消耗更低.
基于Layer的動畫
一個layer對象展示的屏幕上時, 其數(shù)據(jù)和狀態(tài)信息是分離的, 這個分離就導致Core Animation可以用動畫來展示舊狀態(tài)到新狀態(tài)的變化過程. 比如, 改變一個layer的位置屬性就會導致Core Animation把layer從現(xiàn)在的位置移動到新的位置. 下圖展示了幾個在layer上可以實現(xiàn)的動畫類型.

在動畫過程中, Core Animation是在硬件中一幀一幀的繪制. 你需要做的就是指定開始點和結(jié)束點, 剩下的工作就全部交給Core Animation去做.
Layer對象定義了自身的幾何屬性
layer的一個作用就是管理其內(nèi)容的視覺幾何信息. 這些信息包括bounds, 屏幕上的位置, 是否被旋轉(zhuǎn), 縮放或形變. 像view一樣, layer也有frame和bounds, 你可以用它們來指定layer的位置和其內(nèi)容. layer還有view所沒有的屬性, 比如anchor point, 它定義了操作發(fā)生處的位置. 指定一個layer的幾何信息有時也和指定view的信息時有所不同.
Layers使用兩種不同的坐標系統(tǒng)
Layers使用兩種坐標系統(tǒng), 基于點的坐標系統(tǒng)和單位坐標系統(tǒng). 使用哪種坐標系統(tǒng)取決于傳遞什么樣的值進來. 當傳遞具體的數(shù)值時, 使用的就是基于點的坐標系統(tǒng). 當傳遞的值并不是固定的數(shù)值時(相對值), 使用的就是單位坐標系統(tǒng).
在點坐標系統(tǒng)中, 設置一個layer的大小和位置最常用的屬性就是bounds 和 position. bounds包含了layer自身的坐標系統(tǒng)和其在屏幕上的大小. position定義了layer自身相對于其父坐標系統(tǒng)的位置. 盡管layer也有frame屬性, 但是它卻很少被使用, 因為它的值就是從bounds和position屬性中傳來的.
layer的方向是基于它處在哪個平臺上. 下圖展示了iOS和OS X系統(tǒng)上默認的方向. 在iOS系統(tǒng)中, 坐標系統(tǒng)的(0,0)點是在layer的左上角, OS X系統(tǒng)上則是在左下角. 如果將Core Animation的代碼在這兩個平臺之間都使用, 那么要注意坐標系原點是有所不同的.

注:
position屬性是在layer的中心處.
錨點是用單位坐標系統(tǒng)來指定的. Core Animation用單位坐標來代表一些當layer的尺寸改變時其值可能會發(fā)生改變的屬性. 你可以把單位坐標看作是相對坐標. 每個坐標在單位坐標系中都有一個范圍, 即0.0 - 1.0. 比如在x軸方向, 左邊緣就是在坐標系的0.0處, 右邊緣就是在坐標系的1.0處. 而在y軸方向, 值的改變有所不同, 如下圖所示:

所有坐標的值, 不論是點坐標還是單位坐標, 都是以浮點數(shù)的類型設定的. 這樣的設置可以讓你指定更精確的位置屬性.
錨點影響幾何操作
和一個layer幾何屬性相關(guān)的操作都是相對于layer的錨點來發(fā)生的, 錨點是可以通過調(diào)用layer的anchorPoint屬性獲得. 當操作一個layer的位置或者形變時, 錨點的影響是最為顯著的. position屬性總是相對于layer的錨點來設定的. 而任何layer上發(fā)生的形變也是相對于錨點的.
下圖展示了將錨點的值從默認值改變到一個其他值時, 其是如何影響layer的position屬性值的變化的. 盡管相對于父坐標系來說, layer并沒有發(fā)生移動, 但是將錨點從layer的中心移動到layer的原點會改變position的值.

下圖展示了改變錨點是如何影響形變的. 當對一個layer實施旋轉(zhuǎn)操作時, 旋轉(zhuǎn)是圍繞這錨點來旋轉(zhuǎn)的. 因為錨點默認是在layer的中心點, 所以旋轉(zhuǎn)就如何我們期望的樣子一樣. 但如果你改變了錨點的位置, 那么旋轉(zhuǎn)就和我們所想象的旋轉(zhuǎn)有所不同了.

Layers可以在三個維度上進行操作
每個layer有兩個形變矩陣, 你可以用來操作layer以及它的內(nèi)容. CALayer的transform屬性指定了你想實施形變的layer以及它的子layer. 所以當你想改變layer的時候你就可以使用這個屬性. 比如, 你可以使用這個屬性來對layer進行縮放,旋轉(zhuǎn)或者改變其位置. sublayerTransform屬性定義了額外的形變, 其只能應用于子layer上, 該屬性最常用與給一個場景添加一個透視效果.
形變的發(fā)生是通過坐標值和矩陣相乘得到新坐標而實現(xiàn)的. 因為Core Animation的值可以從三個維度上指定, 而每個坐標點有4個值, 所以要乘以一個4x4的矩陣, 如下圖所示. 在Core Animation中, 下圖的形變是通過CATransform3D的類型來展現(xiàn)的. 幸運的是, 你不需要直接來改變以下結(jié)構(gòu)來實現(xiàn)形變. Core Animation提供了一系列易于理解的函數(shù)來實現(xiàn)縮放, 形變, 旋轉(zhuǎn)和矩陣比較. 除了通過函數(shù)操作形變以外, Core Animation還支持KVC. 具體鍵值列表請看CATransform3D Key Paths

下圖展示了一些常見的形變中矩陣的配置信息.

Layer樹反映動畫狀態(tài)的不同方面
一個使用Core Animation的app有三組layer對象. 每一組layer對象在展示內(nèi)容的過程中都扮演著不同的角色.
處在
layer樹上的對象是和app交互最多的對象. 處在這個樹上的對象存儲著任何動畫的目標值. 一旦你改變了一個layer的屬性, 你就會使用到這些對象.處在
展現(xiàn)樹上的對象包含了正在進行的動畫的動態(tài)數(shù)據(jù). 盡管layer樹上的對象包含了一個動畫的目標值, 但是展現(xiàn)樹上的對象反映的是此時此刻顯示在屏幕上的實時的值. 你不應該改變處在展現(xiàn)樹上的對象的值. 相反, 你應該使用這些對象來讀取當前的值, 也許可以以這些值為起點來創(chuàng)建新的動畫.處在
渲染樹上的對象就是在展示實際的動畫, 并且對Core Animation來說是私有的.
每一組layer對象都被有序的組織在一個層級結(jié)構(gòu)中, 就想app中的view一樣. 事實上, 對于一個允許其所有view都帶layer的app來說, 每個樹的初始結(jié)構(gòu)都和view的層級是一致的. 然而, 一個app可以往layer層級中添加額外的layer對象, 這就是和view無關(guān)聯(lián)的layer. 下圖展示了一個簡單的iOS app中l(wèi)ayer的分解. 例子中的window里包含一個content view, 其中包含一個button view和兩個獨立的layer對象. 每一個view都有一個相對應layer對象, 以此來形成layer層級.

對于每一個在layer樹中的對象來說, 在展現(xiàn)樹和渲染樹上都有與之對應的對象. 如下圖所示. 前面也有提到, app主要是和layer樹上的對象打交道, 但偶爾也會和展現(xiàn)樹上的對象交互. 需要指出的是, 在layer樹上獲取一個對象在presentationLayer的屬性會返回在展現(xiàn)樹上相對應的對象. 你可能在動畫進行到一半的時候想獲得現(xiàn)在一個屬性的值.

注意: 只有動畫正在進行的時候, 你才應該獲取
展現(xiàn)樹上的對象. 動畫在進行的過程中,展現(xiàn)樹上的對象包含了此時此刻出現(xiàn)在屏幕上的layer的值. 這不同于layer樹, 它總是返回代碼設置好的結(jié)束值.
Layers 和 Views 的關(guān)系
Layers并不是app的view的替代. 這也就是說, 你不能只是通過一個layer對象就創(chuàng)建一個視覺界面. layer為view提供了基礎(chǔ). 具體來講, layer使得繪圖和動畫都更有效率. 然而, 也有很多事情是layer所不能做的. layer不能處理事件, 不能繪制內(nèi)容, 也不能參與到響應者鏈中等等. 基于這些原因, 每一個App都至少要有一個或者更多個view來處理交互.
在iOS中, 每一個view背后都有一個相對應的layer對象, 而在OS X中你需要自己覺得哪個view需要有l(wèi)ayer. 在OS X v10.8 以及之后的版本中, 給每一個view添加一個layer對象看起來是非常有意義的. 然而, 也并不是必須要這么做. Layer會增加app的體積, 當然它的缺點可以和優(yōu)點相互抵消. 所以比較好的做法就是禁用layer前先對app的表現(xiàn)進行測試.
當你允許view支持layer時, 你就創(chuàng)建了一個基于layer的view. 在基于layer的view中, 系統(tǒng)會創(chuàng)建下層的layer對象并且保持其和view的同步. 所有的iOS view都是基于layer的, 在OS X中的大多數(shù)view也是如此. 然而, 在OS X中, 你可以創(chuàng)建一個layer-hosting view, 其特點就是該view的layer是你自己提供的. 對于這樣的view, 在view改變時, AppKit并不會改變layer.
注意: 對基于layer的view而言, 無論什么時候都推薦你去操作view, 而不是它的layer. 在iOS中, view僅僅是layer對象外一層薄薄的包裝, 所以任何你對layer的操作都是能夠達到效果. 但是在iOS和OS X中, 有時候直接對layer進行操作可能并不會產(chǎn)生理想的效果.
除了和view相關(guān)聯(lián)的layer外, 你也可以創(chuàng)建一個單獨的layer對象, 即這個對象沒有相對應的view. 你可以把這些單獨的layer對象鑲嵌進app中的其他layer對象中, 包括那些帶有相對應view的layer對象. 你通??梢允褂脝为毜膌ayer對象作為一個特定的優(yōu)化路徑的組成部分.
關(guān)于如何讓view支持layer, 可以參照Enabling Core Animation Support in Your App. 而如何創(chuàng)建layer對象層級, 以及一些注意點, 可以參照Building a Layer Hierarchy.