隱式動畫:
所謂的隱式動畫 是因為我們并沒有指定任何動畫的類型,我們僅僅改變了一個屬性,然后Core Animation來決定如何并且何時去做動畫。
但當(dāng)你改變一個屬性,Core Animation是如何判斷動畫類型和持續(xù)時間的呢?實際上動畫執(zhí)行的時間取決于當(dāng)前事務(wù)的設(shè)置,動畫類型取決于圖層行為。
事務(wù)實際上是Core Animation用來包含一系列屬性動畫集合的機制,任何用指定事務(wù)去改變可以做動畫的圖層屬性都不會立刻發(fā)生變化,而是當(dāng)事務(wù)一旦提交的時候開始用一個動畫過渡到新值。
事務(wù)是通過CATransaction類來做管理,這個類的設(shè)計有些奇怪,不像你從它的命名預(yù)期的那樣去管理一個簡單的事務(wù),而是管理了一疊你不能訪問的事務(wù)。CATransaction沒有屬性或者實例方法,并且也不能用+alloc和-init方法創(chuàng)建它。但是可以用+begin和+commit分別來入棧或者出棧。
任何可以做動畫的圖層屬性都會被添加到棧頂?shù)氖聞?wù),你可以通過+setAnimationDuration:方法設(shè)置當(dāng)前事務(wù)的動畫時間,或者通過+animationDuration方法來獲取值(默認(rèn)0.25秒)
如果你用過UIView的動畫方法做過一些動畫效果,那么應(yīng)該對這個模式不陌生。UIView有兩個方法,+beginAnimations:context:和+commitAnimations,和CATransaction的+begin和+commit方法類似。實際上在+beginAnimations:context:和+commitAnimations之間所有視圖或者圖層屬性的改變而做的動畫都是由于設(shè)置了CATransaction的原因。
在iOS4中,蘋果對UIView添加了一種基于block的動畫方法:+animateWithDuration:animations:。這樣寫對做一堆的屬性動畫在語法上會更加簡單,但實質(zhì)上它們都是在做同樣的事情。
CATransaction的+begin和+commit方法在+animateWithDuration:animations:內(nèi)部自動調(diào)用,這樣block中所有屬性的改變都會被事務(wù)所包含。這樣也可以避免開發(fā)者由于對+begin和+commit匹配的失誤造成的風(fēng)險。
原理:
我們把改變屬性時CALayer自動應(yīng)用的動畫稱作行為,當(dāng)CALayer的屬性被修改時候,它會調(diào)用-actionForKey:方法,傳遞屬性的名稱。剩下的操作都在CALayer的頭文件中有詳細(xì)的說明,實質(zhì)上是如下幾步:
圖層首先檢測它是否有委托,并且是否實現(xiàn)CALayerDelegate協(xié)議指定的-actionForLayer:forKey方法。如果有,直接調(diào)用并返回結(jié)果。
如果沒有委托,或者委托沒有實現(xiàn)-actionForLayer:forKey方法,圖層接著檢查包含屬性名稱對應(yīng)行為映射的actions字典。
如果actions字典沒有包含對應(yīng)的屬性,那么圖層接著在它的style字典接著搜索屬性名。
最后,如果在style里面也找不到對應(yīng)的行為,那么圖層將會直接調(diào)用定義了每個屬性的標(biāo)準(zhǔn)行為的-defaultActionForKey:方法。
所以一輪完整的搜索結(jié)束之后,-actionForKey:要么返回空(這種情況下將不會有動畫發(fā)生),要么是CAAction協(xié)議對應(yīng)的對象,最后CALayer拿這個結(jié)果去對先前和當(dāng)前的值做動畫。
于是這就解釋了UIKit是如何禁用隱式動畫的:每個UIView對它關(guān)聯(lián)的圖層都扮演了一個委托,并且提供了-actionForLayer:forKey的實現(xiàn)方法。當(dāng)不在一個動畫塊的實現(xiàn)中,UIView對所有圖層行為返回nil,但是在動畫block范圍之內(nèi),它就返回了一個非空值。