實現動畫方式深度解析(九) —— Core Animation之構建圖層層級 (六)

版本記錄

版本號 時間
V1.0 2017.09.23

前言

app中好的炫的動畫可以讓用戶耳目一新,為產品增色不少,關于動畫的實現我們可以用基本動畫、關鍵幀動畫、序列幀動畫以及基于CoreGraphic的動畫等等,接下來這幾篇我就介紹下我可以想到的幾種動畫繪制方法。具體Demo示例已開源到Github —— 刀客傳奇,感興趣的可以看我寫的另外幾篇。
1. 實現動畫方式深度解析(一) —— 播放GIF動畫(一)
2. 實現動畫方式深度解析(二) —— 播放GIF動畫之框架FLAnimatedImage的使用(二)
3. 實現動畫方式深度解析(三) —— 播放序列幀動畫(一)
4. 實現動畫方式深度解析(四) —— QuartzCore框架(一)
5. 實現動畫方式深度解析(五) —— QuartzCore框架之CoreAnimation(二)
6. 實現動畫方式深度解析(六) —— Core Animation Basics(三)
7. 實現動畫方式深度解析(七) —— Core Animation之Setting Up Layer Objects(四)
8. 實現動畫方式深度解析(八) —— Core Animation之動畫層內容 (五)

Building a Layer Hierarchy - 構建圖層層次結構

大多數情況下,在應用程序中使用圖層的最佳方法是將它們與視圖對象結合使用。 但是,有時您可能需要通過向其添加其他圖層對象來增強視圖層次結構。 在這樣做時,您可以使用使用層提供更好的性能,或者讓您實現一個單獨使用視圖很難處理的功能。 在這種情況下,您需要知道如何管理您創建的層次結構。

重要提示:在OS X v10.8及更高版本中,建議您最小化對層次結構的使用,并僅使用層支持的視圖。 在該版本的OS X中引入的層重繪策略允許您自定義層次支持的視圖的行為,并且仍然可以獲得之前使用獨立圖層獲得的那種性能。


Arranging Layers into a Layer Hierarchy - 將層布置到層次結構中

層次layer結構在很多方面都是和視圖view結構類似的。 您將一層嵌入另一層以在嵌入層(稱為子層)與父層(稱為超層)之間創建親子關系。 這個親子關系影響子層的各個方面。 例如,其內容位于其父級的內容之上,其位置相對于其父級的坐標系來指定,并且受到應用于父級的任何轉換的影響。

1. Adding, Inserting, and Removing Sublayers - 增加、插入和刪除子層

每個層對象都有添加,插入和刪除子層的方法。 下表總結了這些方法及其行為。

行為 方法 描述
增加layer addSublayer: 向當前圖層添加一個新的子圖層對象。 子層被添加到層的子層列表的末尾。 這會導致子層在其zPosition屬性中以相同的值出現在任何兄弟圖層之上
插入layer insertSublayer:above: insertSublayer:atIndex: insertSublayer:below: 在指定的索引或相對于另一個子層的位置插入子層到子層層次結構中。 當插入另一個子層的上方或下方時,您只在子層數組中指定子層的位置。 層的實際可見性主要由其zPosition屬性中的值確定,其次由它們在子層數組中的位置確定。
移除layer removeFromSuperlayer 從父層中移除子層
交換layer replaceSublayer:with: 交換一個子層與另一個子層。 如果要插入的子層已經在另一個層次結構中,則會從該層次結構中先刪除它。

使用您自己創建的圖層對象時,可以使用上述方法。 您不會使用這些方法來排列屬于圖層支持視圖的圖層。 但是,層次支持的視圖可以作為您自己創建的獨立圖層的父級。

2. Positioning and Sizing Sublayers - 定位和調整子層

添加和插入子層時,您必須在子屏幕出現之前設置子層的大小和位置。 在添加到層次結構中之后,您可以修改子層的大小和位置,但是在創建層時應該習慣設置這些值。

您使用bounds屬性設置子層的大小,并使用position屬性將其位置設置在其超層內。 邊界矩形的起點幾乎總是(0,0),而大小是您為點指定的層所需的任何大小。 位置屬性中的值相對于默認情況下位于圖層中心的圖層的錨點進行解釋。 如果不為這些屬性分配值,Core Animation將圖層的初始寬度和高度設置為0,并將位置設置為(0,0)。

myLayer.bounds = CGRectMake(0, 0, 100, 100);
myLayer.position = CGPointMake(200, 200);

重要提示:始終對圖層的寬度和高度使用整數。

3. How Layer Hierarchies Affect Animations - 圖層層級是如何影響到動畫的

某些超層的屬性可能會影響應用于其子層的任何動畫的行為。 一個這樣的屬性是速度屬性,它是動畫速度的乘數。 此屬性的值默認設置為1.0,但將其更改為2.0會導致動畫以原始速度的兩倍運行,從而在一半時間內完成。 此屬性不僅影響其設置的圖層,還會影響該圖層的子圖層。 這種變化也是乘法的。 如果子層及其超層速度為2.0,則子層上的動畫以其原始速度的四倍運行。

大多數其他層次更改可以以可預測的方式影響任何包含的子層。 例如,將旋轉變換應用于層將旋轉該層及其所有子層。 類似地,更改圖層的不透明度會改變其子圖層的不透明度。 對圖層大小的更改遵循Adjusting the Layout of Your Layer Hierarchies中描述的布局規則。


Adjusting the Layout of Your Layer Hierarchies - 調整層次結構的布局

核心動畫支持幾個選項來調整子層的大小和位置以響應其超級層的更改。 在iOS中,普遍使用層次支持的視圖使得創建層次結構不太重要; 僅支持手動布局更新。 對于OS X,還有其他幾個選項可以幫助您更輕松地管理層次結構。

如果您使用創建的獨立圖層對象構建圖層層次結構,則層級布局才是相關的。 如果您的應用程序的圖層與視圖相關聯,請使用基于視圖的布局支持來更新視圖的大小和位置以響應更改。

1. Using Constraints to Manage Your Layer Hierarchies in OS X - 在OS X中使用約束管理您的圖層層級

約束允許您使用層與其上層或同層之間的一組詳細關系來指定層的位置和大小。 定義約束需要以下步驟:

  • 創建一個或多個CAConstraint對象。 使用這些對象來定義約束參數。
  • 將約束對象添加到其修改屬性的圖層上。
  • 檢索共享的CAConstraintLayoutManager對象并分配給直接的上層。

下圖顯示了可用于定義約束的屬性以及它們影響的圖層的方面。 您可以使用約束來根據相對于另一個圖層的中點邊緣的位置來更改圖層的位置。 您也可以使用它們來更改圖層的大小。 您所做的更改可能與父層成正比或與另一層相關。 您甚至可以為結果更改添加縮放因子或常數。 這種額外的靈活性使得可以使用一組簡單的規則非常精確地控制圖層的大小和位置。

Constraint layout manager attributes

每個約束對象沿同一個軸封裝兩個層之間的一個幾何關系。 可以將最多兩個約束對象分配給每個軸,并且它們是確定哪個屬性是可更改的那些約束。 例如,如果為圖層的左右邊緣指定約束,則圖層的大小將更改。 如果您為圖層的左邊緣和寬度指定約束,則圖層右邊緣的位置將更改。 如果為其中一個邊緣指定單個約束,則Core Animation將創建一個隱含的約束,該約束可將圖層的大小固定在給定維中。

創建約束時,必須始終指定三條信息:

  • 要限制的層的方面
  • 該層用作參考
  • 參考層的方面用于比較

下面代碼顯示了一個簡單的約束,將一個圖層的垂直中點定位到其上層的垂直中點。 當引用超級層時,使用字符串超層。 這個字符串是一個專用名稱,用于引用超級層。 使用它不需要有指向圖層的指針或知道圖層的名稱。 它還允許您更改超級層,并將約束自動應用于新的父級。 (當創建相對于兄弟層的約束時,您必須使用其name屬性來標識兄弟層。

//Defining a simple constraint

[myLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidY]];

要在運行時應用約束,必須將共享的CAConstraintLayoutManager對象附加到直接的上層。 每層負責管理子層的布局。 將布局管理器分配給父級會告訴Core Animation應用其子節點定義的約束。 布局管理器對象自動應用約束。 在將其分配給父層之后,您不需要告訴它來更新布局。

要了解限制在更具體的情況下如何工作,請參見下圖。 在該示例中,設計要求層A的寬度和高度保持不變,并且層A在其超層內保持居中。 另外,層B的寬度必須與層A的寬度一致,層B的頂邊必須保持在層A的底邊下方10點,層B的底邊必須重新指向超層底部邊緣下10點。 下面代碼顯示了您將用于創建此示例的子層和約束的代碼。

Example constraints based layout
// Setting up constraints for your layers
 
// Create and set a constraint layout manager for the parent layer.
theLayer.layoutManager=[CAConstraintLayoutManager layoutManager];
 
// Create the first sublayer.
CALayer *layerA = [CALayer layer];
layerA.name = @"layerA";
layerA.bounds = CGRectMake(0.0,0.0,100.0,25.0);
layerA.borderWidth = 2.0;
 
// Keep layerA centered by pinning its midpoint to its parent's midpoint.
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidY]];
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidX]];
[theLayer addSublayer:layerA];
 
// Create the second sublayer
CALayer *layerB = [CALayer layer];
layerB.name = @"layerB";
layerB.borderWidth = 2.0;
 
// Make the width of layerB match the width of layerA.
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintWidth]];
 
// Make the horizontal midpoint of layerB match that of layerA
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintMidX]];
 
// Position the top edge of layerB 10 points from the bottom edge of layerA.
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintMinY
                                                     offset:-10.0]];
 
// Position the bottom edge of layerB 10 points
//  from the bottom edge of the parent layer.
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMinY
                                                     offset:+10.0]];
 
[theLayer addSublayer:layerB];

關于上面代碼的一個有趣的事情是代碼不會明確地設置layerB的大小。 由于定義的約束,每次布局更新時都會自動設置layerB的寬度和高度。 因此,使用邊界矩形設置大小是不必要的。

警告:創建約束時,不要在約束之間創建循環引用。 循環約束使得無法計算所需的布局信息。 遇到這樣的循環引用時,布局行為是未定義的。

2. Setting Up Autoresizing Rules for Your OS X Layer Hierarchies - 為您的OS X層次結構設置自動調整規則

自動調整規則是在OS X中調整圖層大小和位置的另一種方法。通過自動調整規則,您可以指定圖層的邊緣是否應保持與超層相應邊緣固定或可變距離。 您可以類似地指定圖層的寬度或高度是固定的還是可變的。 關系總是在層和它的超層之間。 您不能使用自動調整規則來指定兄弟層之間的關系。

3. Manually Laying Out Your Layer Hierarchies - 手動布局層次結構

在iOS和OS X上,您可以通過在超級層的委托對象上實現layoutSublayersOfLayer:方法來手動處理布局。 您可以使用該方法來調整當前嵌入在圖層中的任何子圖層的大小和位置。 當進行手動布局更新時,由您執行必要的計算來定位每個子層。

如果要實現自定義層子類,則您的子類可以覆蓋layoutSublayers方法,并使用該方法(而不是委托)來處理任何布局任務。 在您需要完全控制自定義圖層類中子圖層的位置的情況下,才應該覆蓋此方法。 替換默認實現會阻止Core Animation在OS X上應用約束或自動調整規則。


Sublayers and Clipping - 子圖層和裁剪

與視圖不同,超層不會自動剪貼位于其邊界矩形之外的子圖層的內容。 相反,默認情況下,超級層允許其子層完全顯示。 但是,您可以通過將圖層的masksToBounds屬性設置為YES來重新啟用裁剪。

層的剪裁蒙版的形狀包括層的角半徑,如果指定了它的角半徑。 下圖顯示了一個層,它演示了maskToBounds屬性如何影響具有圓角的圖層。 當屬性設置為NO時,子圖層將全部顯示,即使它們超出了其父層的邊界。 將屬性更改為YES會導致其內容被剪切。

Clipping sublayers to the parent’s bounds

Converting Coordinate Values Between Layers - 層之間轉換坐標值

偶爾,您可能需要將一層中的坐標值轉換為不同圖層中相同屏幕位置的坐標值。 CALayer類提供了一組可用于此目的的簡單轉換程序:

除了轉換點和矩形值之外,還可以使用convertTime:fromLayer:convertTime:toLayer:方法來轉換圖層之間的時間值。 每個層定義自己的本地時間空間,并使用該時間空間將動畫的開始和結束與系統的其余部分同步。 這些時間空間默認同步; 但是,如果更改一組圖層的動畫速度,那么這些圖層的時間空間將相應更改。 您可以使用時間轉換方法來解決任何這些因素,并確保兩個層的時間同步。

后記

未完,待續~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容