版本記錄
版本號 | 時間 |
---|---|
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對象并分配給直接的上層。
下圖顯示了可用于定義約束的屬性以及它們影響的圖層的方面。 您可以使用約束來根據相對于另一個圖層的中點邊緣的位置來更改圖層的位置。 您也可以使用它們來更改圖層的大小。 您所做的更改可能與父層成正比或與另一層相關。 您甚至可以為結果更改添加縮放因子或常數。 這種額外的靈活性使得可以使用一組簡單的規則非常精確地控制圖層的大小和位置。
每個約束對象沿同一個軸封裝兩個層之間的一個幾何關系。 可以將最多兩個約束對象分配給每個軸,并且它們是確定哪個屬性是可更改的那些約束。 例如,如果為圖層的左右邊緣指定約束,則圖層的大小將更改。 如果您為圖層的左邊緣和寬度指定約束,則圖層右邊緣的位置將更改。 如果為其中一個邊緣指定單個約束,則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點。 下面代碼顯示了您將用于創建此示例的子層和約束的代碼。
// 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會導致其內容被剪切。
Converting Coordinate Values Between Layers - 層之間轉換坐標值
偶爾,您可能需要將一層中的坐標值轉換為不同圖層中相同屏幕位置的坐標值。 CALayer類提供了一組可用于此目的的簡單轉換程序:
除了轉換點和矩形值之外,還可以使用convertTime:fromLayer:和convertTime:toLayer:方法來轉換圖層之間的時間值。 每個層定義自己的本地時間空間,并使用該時間空間將動畫的開始和結束與系統的其余部分同步。 這些時間空間默認同步; 但是,如果更改一組圖層的動畫速度,那么這些圖層的時間空間將相應更改。 您可以使用時間轉換方法來解決任何這些因素,并確保兩個層的時間同步。
后記
未完,待續~~