事務(隱式動畫),事務動畫
事務
Core Animation基於一個假設,說螢幕上的任何東西都可以(或者可能)做動畫。動畫並不需要你在Core Animation中手動開啟,相反需要明確地關閉,否則他會一直存在。
當你改變CALayer
的一個可做動畫的屬性,它並不能立刻在螢幕上體現出來。相反,它是從先前的值平滑過渡到新的值。這一切都是預設的行為,你不需要做額外的操作。
這看起來這太棒了,似乎不太真實,我們來用一個demo解釋一下:首先和第一章“圖層樹”一樣建立一個藍色的方塊,然後添加一個按鈕,隨機改變它的顏色。代碼見清單7.1。點擊按鈕,你會發現圖層的顏色平滑過渡到一個新值,而不是跳變(圖7.1)。
清單7.1 隨機改變圖層顏色
1 @interface ViewController () 2 3 @property (nonatomic, weak) IBOutlet UIView *layerView; 4 @property (nonatomic, weak) IBOutlet CALayer *colorLayer;/*熱心人發現這裡應該改為@property (nonatomic, strong) CALayer *colorLayer;否則運行結果不正確。 5 */ 6 @end 7 8 @implementation ViewController 9 10 - (void)viewDidLoad11 {12 [super viewDidLoad];13 //create sublayer14 self.colorLayer = [CALayer layer];15 self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);16 self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;17 //add it to our view18 [self.layerView.layer addSublayer:self.colorLayer];19 }20 21 - (IBAction)changeColor22 {23 //randomize the layer background color24 CGFloat red = arc4random() / (CGFloat)INT_MAX;25 CGFloat green = arc4random() / (CGFloat)INT_MAX;26 CGFloat blue = arc4random() / (CGFloat)INT_MAX;27 self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; 28 }29 30 @end
圖7.1 添加一個按鈕來控製圖層顏色
這其實就是所謂的隱式動畫。之所以叫隱式是因為我們並沒有指定任何動畫的類型。我們僅僅改變了一個屬性,然後Core Animation來決定如何並且何時去做動畫。Core Animaiton同樣支援顯式動畫,下章詳細說明。
但當你改變一個屬性,Core Animation是如何判斷動畫類型和期間的呢?實際上動畫執行的時間取決於當前事務的設定,動畫類型取決於圖層行為。
事務實際上是Core Animation用來包含一系列屬性動畫集合的機制,任何用指定事務去改變可以做動畫的圖層屬性都不會立刻發生變化,而是當事務一旦提交的時候開始用一個動畫過渡到新值。
事務是通過CATransaction
類來做管理,這個類的設計有些奇怪,不像你從它的命名預期的那樣去管理一個簡單的事務,而是管理了一疊你不能訪問的事務。CATransaction
沒有屬性或者執行個體方法,並且也不能用+alloc
和-init
方法建立它。但是可以用+begin
和+commit
分別來入棧或者出棧。
任何可以做動畫的圖層屬性都會被添加到棧頂的事務,你可以通過+setAnimationDuration:
方法設定當前事務的動畫時間,或者通過+animationDuration
方法來擷取值(預設0.25秒)。
Core Animation在每個run loop周期中自動開始一次新的事務(run loop是iOS負責收集使用者輸入,處理定時器或者網路事件並且重新繪製螢幕的東西),即使你不顯式的用[CATransaction begin]
開始一次事務,任何在一次run loop迴圈中屬性的改變都會被集中起來,然後做一次0.25秒的動畫。
明白這些之後,我們就可以輕鬆修改變色動畫的時間了。我們當然可以用當前事務的+setAnimationDuration:
方法來修改動畫時間,但在這裡我們首先起一個新的事務,於是修改時間就不會有別的副作用。因為修改當前事務的時間可能會導致同一時刻別的動畫(如旋轉螢幕),所以最好還是在調整動畫之前壓入一個新的事務。
修改後的代碼見清單7.2。運行程式,你會發現色塊顏色比之前變得更慢了。
清單7.2 使用CATransaction
控制動畫時間
1 - (IBAction)changeColor 2 { 3 //begin a new transaction 4 [CATransaction begin]; 5 //set the animation duration to 1 second 6 [CATransaction setAnimationDuration:1.0]; 7 //randomize the layer background color 8 CGFloat red = arc4random() / (CGFloat)INT_MAX; 9 CGFloat green = arc4random() / (CGFloat)INT_MAX;10 CGFloat blue = arc4random() / (CGFloat)INT_MAX;11 self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;12 //commit the transaction13 [CATransaction commit];14 }
如果你用過UIView
的動畫方法做過一些動畫效果,那麼應該對這個模式不陌生。UIView
有兩個方法,+beginAnimations:context:
和+commitAnimations
,和CATransaction
的+begin
和+commit
方法類似。實際上在+beginAnimations:context:
和+commitAnimations
之間所有視圖或者圖層屬性的改變而做的動畫都是由於設定了CATransaction
的原因。
在iOS4中,蘋果對UIView添加了一種基於block的動畫方法:+animateWithDuration:animations:
。這樣寫對做一堆的屬性動畫在文法上會更加簡單,但實質上它們都是在做同樣的事情。
CATransaction
的+begin
和+commit
方法在+animateWithDuration:animations:
內部自動調用,這樣block中所有屬性的改變都會被事務所包含。這樣也可以避免開發人員由於對+begin
和+commit
匹配的失誤造成的風險。