實(shí)現(xiàn)動畫方式深度解析(九) —— Core Animation之構(gòu)建圖層層級 (六)

版本記錄

版本號 時間
V1.0 2017.09.23

前言

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

Building a Layer Hierarchy - 構(gòu)建圖層層次結(jié)構(gòu)

大多數(shù)情況下,在應(yīng)用程序中使用圖層的最佳方法是將它們與視圖對象結(jié)合使用。 但是,有時您可能需要通過向其添加其他圖層對象來增強(qiáng)視圖層次結(jié)構(gòu)。 在這樣做時,您可以使用使用層提供更好的性能,或者讓您實(shí)現(xiàn)一個單獨(dú)使用視圖很難處理的功能。 在這種情況下,您需要知道如何管理您創(chuàng)建的層次結(jié)構(gòu)。

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


Arranging Layers into a Layer Hierarchy - 將層布置到層次結(jié)構(gòu)中

層次layer結(jié)構(gòu)在很多方面都是和視圖view結(jié)構(gòu)類似的。 您將一層嵌入另一層以在嵌入層(稱為子層)與父層(稱為超層)之間創(chuàng)建親子關(guān)系。 這個親子關(guān)系影響子層的各個方面。 例如,其內(nèi)容位于其父級的內(nèi)容之上,其位置相對于其父級的坐標(biāo)系來指定,并且受到應(yīng)用于父級的任何轉(zhuǎn)換的影響。

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

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

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

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

2. Positioning and Sizing Sublayers - 定位和調(diào)整子層

添加和插入子層時,您必須在子屏幕出現(xiàn)之前設(shè)置子層的大小和位置。 在添加到層次結(jié)構(gòu)中之后,您可以修改子層的大小和位置,但是在創(chuàng)建層時應(yīng)該習(xí)慣設(shè)置這些值。

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

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

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

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

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

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


Adjusting the Layout of Your Layer Hierarchies - 調(diào)整層次結(jié)構(gòu)的布局

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

如果您使用創(chuàng)建的獨(dú)立圖層對象構(gòu)建圖層層次結(jié)構(gòu),則層級布局才是相關(guān)的。 如果您的應(yīng)用程序的圖層與視圖相關(guān)聯(lián),請使用基于視圖的布局支持來更新視圖的大小和位置以響應(yīng)更改。

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

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

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

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

Constraint layout manager attributes

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

創(chuàng)建約束時,必須始終指定三條信息:

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

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

//Defining a simple constraint

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

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

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

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];

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

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

2. Setting Up Autoresizing Rules for Your OS X Layer Hierarchies - 為您的OS X層次結(jié)構(gòu)設(shè)置自動調(diào)整規(guī)則

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

3. Manually Laying Out Your Layer Hierarchies - 手動布局層次結(jié)構(gòu)

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

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


Sublayers and Clipping - 子圖層和裁剪

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

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

Clipping sublayers to the parent’s bounds

Converting Coordinate Values Between Layers - 層之間轉(zhuǎn)換坐標(biāo)值

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

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

后記

未完,待續(xù)~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容