1. 程式人生 > >Core Animation 文檔翻譯 (第四篇)

Core Animation 文檔翻譯 (第四篇)

為我 times mean app gre mage 屬性動畫 class mea

Core Animation 文檔翻譯(第四篇)




讓Layer的content動畫起來


核心動畫的基礎接口以及為擁有Layer的View做的動畫擴展接口,使得為Layer制作復雜動畫變得簡單化。例如改變Layer的frame的size、改變Layer在屏幕上的position、應用旋轉transform、或者改變它的opacity。通過使用核心動畫,創建一個動畫效果將會變得簡單的就像修改屬性一樣,但是我們也能顯式的創建和設置動畫參數。
關於創建更多高級動畫可以參見Advanced Animation Tricks(後續會有譯文)。


Layer屬性改變的動畫


根據需要,我們可以選擇顯式或者隱式的執行簡單的動畫。隱式的動畫使用默認的速度調節和動畫屬性來執行動畫;相反,隱式動畫需要我們使用核心動畫對象配置相應的屬性。因此當我們想要使用少量代碼和默認定時速度做動畫過度改變的時候,隱式動畫是個不錯的選擇。

簡單的動畫意味著改變Layer的屬性,和讓核心動畫是這些改變隨著時間發生變化。Layers定義了許多能夠影響可視化外觀的屬性,改變這些屬性之一就會引起相應的外觀動畫性的變化;例如,改變Layer的opacity由1.0到0.0將會引起Layer漸漸隱藏(淡出)並變透明。

重要提示:我們有時可以直接使用核心動畫的接口為Layer-backed View(iOS 內的view都可稱為此種view,前面章節有說明)做動畫,這麽做常常需要一些額外的步驟,更多關於如何使用與Layer-backed views結合的動畫可以參見如何為Layer-Backed View做動畫。

為了觸發隱式動畫,我們只需要去更新Layer對象的屬性。當更改圖層樹上的Layer對象的時候,Layer的屬性將會立刻變更為我們調整的值,然而Layer對象的外觀不會立刻改變為我們設置的值;相反,核心動畫使用我們調整的值作為觸發器來創建和規劃一個或多個隱式動畫,然後核心動畫並執行這些動畫。因此,像代碼3-1中調整屬性值將會引起核心動畫創建動畫對象,並規劃那個動畫到下個更新循環開始執行。

Listing 3-1 Animating a change implicitly

theLayer.opacity = 0.0;

為了制作和隱式動畫一樣的動畫,我們也可通過顯示的使用動畫對象,創建一個CABasicAnimation 對象和使用那個對象配置動畫參數。在將動畫對象添加到Layer上之前,我們可以為動畫對象設置開始和結束值、改變動畫周期,或者任何其他的動畫參數。代碼3-2展示了如何通過動畫對象將Layer漸隱。當創建動畫對象後,我們指定要做動畫的屬性的Key path,然後設置動畫參數。為了執行動畫,我們使用addAnimation:forKey:方法將動畫對象添加到Layer上。

Listing 3-2 Animating a change explicitly

CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
[theLayer addAnimation:fadeAnim forKey:@"opacity"];
 
// Change the actual data value in the layer to the final value.
theLayer.opacity = 0.0;

提示:當創建顯示動畫的時候,建議為動畫對象設置fromValue屬性值,如果我們不指定這個值,核心動畫將會使用Layer的當前值作為動畫的開始值,如果我們將這個屬性值設定的和動畫的最終值一致,那麽將不會產生我們想要的動畫效果。

和隱式動畫不一樣的是,隱式動畫將會更改Layer對象的值,顯示動畫不會修改圖層樹上的數據。顯示動畫僅僅產生動畫效果。在動畫的結束後,核心動畫將會從Layer上移除動畫對象並使用Layer的當前值重新繪制。如果我們想要顯示動畫的改變發生到Layer上,那麽我們必須也更新Layer的屬性,就像3-2中指出的一樣。

隱式和顯示動畫通常在當前runloop循環結束後就開始執行,為了動畫對象的執行,當前線程必須有一個runloop。如果我們改變多個屬性,如果為Layer添加多個動畫對象,所有這些屬性將會同時發生動畫。例如我們通過同時設置兩個動畫,就可以使得隱藏一個Layer和將Layer移動到屏幕外面的同時發生。我們一可以設置動畫對象在某個特殊的時間點開始。關於更多的修改動畫的時間函數參見 Customizing the Timing of an Animation(後續會有譯文)。


使用關鍵幀動畫改編Layer的屬性


屬性動畫通過改編屬性從開始值到結束值發生動畫, CAKeyframeAnimation 對象將會通過設定線性或者非線性目標值點集合的方式制作動畫。一個關鍵幀動畫由目標集合點,和與單個目標點對應的時間點的集合組成。最簡單的配置就是,我們僅僅通過使用數組指定這兩種值。對於改變Layer的position來說,我們也可以指定它沿著path發生變化。動畫對象取我們指定的關鍵幀並通過一個值到下一個值在給定的時間片刻上進行插值建立動畫。

圖3-1顯示了Layer的position屬性5s的動畫。Position是沿著一條path做的動畫,就是使用CGPathRef數據類型制定的path。對應的代碼見代碼3-3。

Figure 3-1 5-second keyframe animation of a layer’s position property
技術分享圖片
技術分享圖片

代碼3-3展示了如何實現圖3-1中動畫的代碼。在這個例子中,path對象是被用來定義每幀動畫中Layer的position。

Listing 3-3 Creating a bounce keyframe animation

// create a CGPath that implements two arcs (a bounce)
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,74.0,74.0);
CGPathAddCurveToPoint(thePath,NULL,74.0,500.0,
                                   320.0,500.0,
                                   320.0,74.0);
CGPathAddCurveToPoint(thePath,NULL,320.0,500.0,
                                   566.0,500.0,
                                   566.0,74.0);
 
CAKeyframeAnimation * theAnimation;
 
// Create the animation object, specifying the position property as the key path.
theAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
theAnimation.path=thePath;
theAnimation.duration=5.0;
 
// Add the animation to the layer.
[theLayer addAnimation:theAnimation forKey:@"position"];


指定關鍵幀的值


關鍵幀的值是關鍵幀動畫最重要的一部分。關鍵幀的值明確了動畫執行的路線。主要指定關鍵幀值的方式是使用數組,但是對於當數組中包含CGPoint數據類型的時候(例如Layer的anchorPoint和position屬性),我們也可以指定CGPathRef數據類型代替。

當指定數組值的時候,數組中應該放的數據類型取決於要做動畫的屬性。我們可以將某些對象直接添加到數組中;但是有些類型必須先轉換到id對象類型在添加之前,所有的標量類型和結構體必須先包裝成對象,例如:

  • 對於對應CGRect的屬性(例如bounds和frame屬性),需要一NSValue對象包裝每個值。
  • 對於Layer的transform屬性,則需要將CATransform3D矩陣包裝為NSValue對象。動畫這個屬性引起關鍵幀動畫按順序將每個transform矩陣變換應用到Layer上。
  • 對於borderCorlor屬性,在加入數組前,需要將每個CGColorRef數據類型映射為id類型。
  • 對於對應CGFloat類型的屬性,再加入數組前,需要將每個值包裝為NSNumber對象。
  • 當要為Layer的contents屬性做動畫的時候,數組中需要添加CGImageRef類型的數據。

對於對應CGPoint數據類型的屬性,我們可以創建point的數組(先包裝成NSValue對象)或者使用CGPathRef對象指定動畫的路線。當我們指定point的數組的時候,關鍵幀動畫對象創建的動畫將會走直線在相鄰的點。當我們指定CGPathRef對象,動畫從路徑的起點開始,並沿著他的輪廓走,如果是曲線,它也能夠沿著曲線的路徑走。我們也可以使用非閉合或者閉合的路徑。

指定關鍵幀動畫的時間函數


關鍵幀的時間函數和空間位置是比基本動畫(basic animations)更復雜的,這裏有幾個供控制它們的屬性:

  • calculationMode屬性用來指定計算動畫函數的算法。它的值將影響其他和時間相關的屬性如何變化。
    • 線性和多階動畫——當動畫calculationMode屬性被設置為kCAAnimationLinear 或 kCAAnimationCubic時,將啟用被提供的時間(timeing)的信息生成動畫,這種模式將會為我們提供對於動畫時間函數最靈活的控制。
    • 平緩的動畫——當動畫calculationMode屬性被設置為kCAAnimationPaced或kCAAnimationCubicPaced時,動畫將不依賴keyTimes或timingFunctions屬性額外提供的時間函數值。相反的時間函數的插值將會以一個常量速度隱式的計算得出。
    • 離散動畫——當動畫calculationMode屬性被設置為kCAAnimationDiscrete時,此時動畫將會在沒有任何插值的情況下,從一個關鍵幀跳到下一個關鍵幀。這個計算模式使用keyTimes屬性裏面的值,但是忽略timingFunctions屬性。
  • keyTimes 屬性指定時間標記,對應於這些時間標記值會應用到每一幀的值。僅僅當calculationMode屬性被設置為 kCAAnimationLinear, kCAAnimationDiscrete, or kCAAnimationCubic,這個屬性才會被才用。對於平緩動畫它是無用的。
  • timingFunctions屬性將使用時間函數曲線制作每一幀。(該屬性將會取代父類的timingFunction屬性。)

如果我們想要自己控制時間函數,需要采用 kCAAnimationLinear或kCAAnimationCubic 模式和keyTimes以及timingFunctions屬性。keytimes屬性指定應用於對應幀值的時間點。timeingFunctions用於控制所有時間的插值,它將允許在每個片段我們應用緩入或者緩出。如果我們不指定timingFunctions時間函數將會是線性的。


移除顯示的動畫(當顯示動畫在執行過程中)


正常情況下動畫會運行到動畫完成,但是如果有需要,我們能夠通過以下方式提前移除它們:

  • 從Layer上移除單個動畫對象,調用Layer的removeAnimationForKey: 方法以便移除動畫對象。這個方法需要使用之前被提供到 addAnimation:forKey:方法的key,因為再添加動畫的時候key就在這個Layer上對應到該動畫對象。key不能為nil。
  • 從Layer上移除所有的動畫對象,只需要調用Layer的removeAllAnimations方法。這個方法將會立即移除所有正在進行的動畫並使用當前狀態信息重繪Layer。

註意:我們不能從Layer直接移除隱式動畫。

當我們從Layer上面移除動畫的時候,核心動畫將會響應——使用Layer當前的值重繪Layer。因為通常是動畫的最終值(end values),這可能引起Layer的樣貌發生跳躍性的變化。如果我們想要Layer的樣貌保持在動畫的最後一幀,我們可以使用呈現樹上的Layer對象獲取最終值(如果是移除操作也可以稱為,動畫當前值)並設置他們這些值到圖層樹。

更多關於臨時暫停動畫可參見代碼5-4(後續會有譯文)。


將多個調整綁定在一起做動畫


如果我們想要將多個動畫同時應用到Layer對象,我們可以將他們綁定在一起通過使用CAAnimationGroup對象,group對象可以以簡單的配置簡化多個動畫對象的管理。設置到group對象的時間函數和duration值將會覆蓋單個動畫對象對應的值。

代碼3-4展示了如何使用一個group對象以相同的時間函數和相同時間周期的實現兩個邊框相關的動畫。

Listing 3-4 Animating two animations together

// Animation 1
CAKeyframeAnimation* widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
NSArray* widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0, @30.0, @0.5, @15.0, @2.0, @50.0, @0.0, nil];
widthAnim.values = widthValues;
widthAnim.calculationMode = kCAAnimationPaced;
 
// Animation 2
CAKeyframeAnimation* colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
            (id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor,  nil];
colorAnim.values = colorValues;
colorAnim.calculationMode = kCAAnimationPaced;
 
// Animation group
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
group.duration = 5.0;
 
[myLayer addAnimation:group forKey:@"BorderChanges"];

Transaction對象能夠提供將動畫組合在一起的更高級方式。Transcation通過允許我們創建嵌套的動畫集和為每個動畫關聯不同動畫參數,也因此Transcation更靈活,更多關於如何使用Transaction對象,可參見 Explicit Transactions Let You Change Animation Parameters(後續會有譯文)。


獲悉動畫的開始和結束時機


核心動畫提供獲取動畫開始和結束事件的支持。這些通知對於動畫輔助任務來說是很好的時間點。例如我們可以借助獲取到開始通知時去設置一些相關聯的狀態信息並使用對應的結束通知撤銷這些狀態。

有一下兩種不同的獲取方式:

  • 添加完成的block到當前的transaction通過使用setCompletionBlock:方法。當transaction中所有的動畫結束的時候,transaction就會執行我們的完成block。
  • 為CAAnimation對象關聯代理並實現 animationDidStart:和animationDidStop:finished:代理方法。

如果我們想要去將兩個動畫銜接在一起,實現當一個結束另外一個就開啟,不要使用動畫通知,相反的應該使用動畫對象的beginTime屬性去開啟每一個動畫在期望的時間點。為了將兩個動畫前後銜接在一起,設置第二個動畫的start為第一個動畫的end時間。更多關於動畫和時間值的信息參見Customizing the Timing of an Animation(後續會有譯文)。


如何為Layer-Backed View做動畫


如果Layer屬於Layer-backed view,動畫創建的推薦方式是使用通過UIKit或AppKit提供的基於View的動畫接口。通過使用核心動畫接口,有許多方式可以為Layer做動畫,但是如何創建這些動畫取決於所在的目標平臺(iOS 、OSX)。


在iOS上修改Layer的規則


因為iOS中的view一直都有一個內在的Layer,UiView類直接從layer對象直接獲取許多數據,這就導致我們為Layer做的調整也會在View對象上自動提現出來,這就意味著我們可以使用核心動畫或者UIView的接口來實現這些調整。

基於Layer-backed View,如果我們想要使用核心動畫創建動畫,我們必須在基於view的動畫block內部發起所有的核心動畫的調用。UIView類默認禁用了Layer動畫,但是在動畫bolcks中可以激活,因此任何在bolcks外的Layer調整將不會發生動畫。代碼3-5展示了基於Layer-backed View內如何為Layer制作opacity隱式動畫,和position顯示動畫。在這個例子中,myNewPosition變量是被提前計算並被block捕獲到的。兩個動畫在同一時間開始,但是opacity動畫一默認的時間函數運行,position動畫以他動畫對象所指定的時間函數運行。

Listing 3-5 Animating a layer attached to an iOS view

[UIView animateWithDuration:1.0 animations:^{
   // Change the opacity implicitly.
   myView.layer.opacity = 0.0;
 
   // Change the position explicitly.
   CABasicAnimation* theAnim = [CABasicAnimation animationWithKeyPath:@"position"];
   theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position];
   theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition];
   theAnim.duration = 3.0;
   [myView.layer addAnimation:theAnim forKey:@"AnimateFrame"];
}];


作為動畫的一部分,記著著更新View的約束


如果使用基於約束的布局規則管理views的position,那麽作為配置動畫的一部分,我們必須移除任何影響動畫的約束。約束影響任何我們為view的position或size做的調整,他們常常影響view之間以及view和他們子view的關系;如果我們為這些帶有約束的view做動畫,我們需要移除這些約束,並應用我們需要的新的約束。

更多關於約束和如何使用他們管理views的layout的信息參見Auto Layout Guide。

Core Animation 文檔翻譯 (第四篇)