咱們來對顏色漸變的例子使用一個不一樣的行爲,經過給colorLayer設置一個自定義的actions字典。咱們也可使用委託來實現,可是actions字典能夠寫更少的代碼。那麼到底改如何建立一個合適的行爲對象呢? git
行爲一般是一個被Core Animation隱式調用的顯式動畫對象。這裏咱們使用的是一個實現了CATransaction的實例,叫作推動過渡。 github
第八章中將會詳細解釋過渡,不過對於如今,知道CATransition響應CAAction協議,而且能夠當作一個圖層行爲就足夠了。結果很贊,不論在何時改變背景顏色,新的色塊都是從左側滑入,而不是默認的漸變效果。 app
清單7.6 實現自定義行爲 dom
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *layerView; @property (nonatomic, weak) IBOutlet CALayer *colorLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //create sublayer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; //add a custom action CATransition *transition = [CATransition animation]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromLeft; self.colorLayer.actions = @{@"backgroundColor": transition}; //add it to our view [self.layerView.layer addSublayer:self.colorLayer]; } - (IBAction)changeColor { //randomize the layer background color CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; }
@end
圖7.3 使用推動過渡的色值動畫 動畫
CALayer的屬性行爲其實很不正常,由於改變一個圖層的屬性並無馬上生效,而是經過一段時間漸變動新。這是怎麼作到的呢? atom
當你改變一個圖層的屬性,屬性值的確是馬上更新的(若是你讀取它的數據,你會發現它的值在你設置它的那一刻就已經生效了),可是屏幕上並無立刻發生改變。這是由於你設置的屬性並無直接調整圖層的外觀,相反,他只是定義了圖層動畫結束以後將要變化的外觀。 spa
當設置CALayer的屬性,其實是在定義當前事務結束以後圖層如何顯示的模型。Core Animation扮演了一個控制器的角色,而且負責根據圖層行爲和事務設置去不斷更新視圖的這些屬性在屏幕上的狀態。 code
咱們討論的就是一個典型的微型MVC模式。CALayer是一個鏈接用戶界面(就是MVC中的view)虛構的類,可是在界面自己這個場景下,CALayer的行爲更像是存儲了視圖如何顯示和動畫的數據模型。實際上,在蘋果本身的文檔中,圖層樹一般都是值的圖層樹模型。 對象
在iOS中,屏幕每秒鐘重繪60次。若是動畫時長比60分之一秒要長,Core Animation就須要在設置一次新值和新值生效之間,對屏幕上的圖層進行從新組織。這意味着CALayer除了「真實」值(就是你設置的值)以外,必需要知道當前顯示在屏幕上的屬性值的記錄。 事務
每一個圖層屬性的顯示值都被存儲在一個叫作呈現圖層的獨立圖層當中,他能夠經過-presentationLayer方法來訪問。這個呈現圖層其實是模型圖層的複製,可是它的屬性值表明了在任何指定時刻當前外觀效果。換句話說,你能夠經過呈現圖層的值來獲取當前屏幕上真正顯示出來的值(圖7.4)。
咱們在第一章中提到除了圖層樹,另外還有呈現樹。呈現樹經過圖層樹中全部圖層的呈現圖層所造成。注意呈現圖層僅僅當圖層首次被提交(就是首次第一次在屏幕上顯示)的時候建立,因此在那以前調用-presentationLayer將會返回nil。
你可能注意到有一個叫作–modelLayer的方法。在呈現圖層上調用–modelLayer將會返回它正在呈現所依賴的CALayer。一般在一個圖層上調用-modelLayer會返回–self(實際上咱們已經建立的原始圖層就是一種數據模型)。
圖7.4 一個移動的圖層是如何經過數據模型呈現的
大多數狀況下,你不須要直接訪問呈現圖層,你能夠經過和模型圖層的交互,來讓Core Animation更新顯示。兩種狀況下呈現圖層會變得頗有用,一個是同步動畫,一個是處理用戶交互。
咱們能夠用一個簡單的案例來證實後者(見清單7.7)。在這個例子中,點擊屏幕上的任意位置將會讓圖層平移到那裏。點擊圖層自己能夠隨機改變它的顏色。咱們經過對呈現圖層調用-hitTest:來判斷是否被點擊。
若是修改代碼讓 -hitTest: 直接做用於colorLayer而不是呈現圖層,你會發現當圖層移動的時候它並不能正確顯示。這時候你就須要點擊圖層將要移動到的位置而不是圖層自己來響應點擊(這就是爲何用呈現圖層來響應交互的緣由)。
清單7.7 使用presentationLayer圖層來判斷當前圖層位置
@interface ViewController () @property (nonatomic, strong) CALayer *colorLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //create a red layer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(0, 0, 100, 100); self.colorLayer.position = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2); self.colorLayer.backgroundColor = [UIColor redColor].CGColor; [self.view.layer addSublayer:self.colorLayer]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //get the touch point CGPoint point = [[touches anyObject] locationInView:self.view]; //check if we've tapped the moving layer if ([self.colorLayer.presentationLayer hitTest:point]) { //randomize the layer background color CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; } else { //otherwise (slowly) move the layer to new position [CATransaction begin]; [CATransaction setAnimationDuration:4.0]; self.colorLayer.position = point; [CATransaction commit]; } } @end
這一章討論了隱式動畫,還有Core Animation對指定屬性選擇合適的動畫行爲的機制。同時你知道了UIKit是如何充分利用Core Animation的隱式動畫機制來強化它的顯式系統,以及動畫是如何被默認禁用而且當須要的時候啓用的。最後,你瞭解了呈現和模型圖層,以及Core Animation是如何經過它們來判斷出圖層當前位置以及將要到達的位置。
在下一章中,咱們將研究Core Animation提供的顯式動畫類型,既能夠直接對圖層屬性作動畫,也能夠覆蓋默認的圖層行爲。