W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
現(xiàn)在來做個實驗,試著直接對UIView關聯(lián)的圖層做動畫而不是一個單獨的圖層。清單7.4是對清單7.2代碼的一點修改,移除了colorLayer
,并且直接設置layerView
關聯(lián)圖層的背景色。
清單7.4 直接設置圖層的屬性
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//set the color of our layerView backing layer directly
self.layerView.layer.backgroundColor = [UIColor blueColor].CGColor;
}
- (IBAction)changeColor
{
//begin a new transaction
[CATransaction begin];
//set the animation duration to 1 second
[CATransaction setAnimationDuration:1.0];
//randomize the layer background color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.layerView.layer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//commit the transaction
[CATransaction commit];
}
運行程序,你會發(fā)現(xiàn)當按下按鈕,圖層顏色瞬間切換到新的值,而不是之前平滑過渡的動畫。發(fā)生了什么呢?隱式動畫好像被UIView
關聯(lián)圖層給禁用了。
試想一下,如果UIView
的屬性都有動畫特性的話,那么無論在什么時候修改它,我們都應該能注意到的。所以,如果說UIKit建立在Core Animation(默認對所有東西都做動畫)之上,那么隱式動畫是如何被UIKit禁用掉呢?
我們知道Core Animation通常對CALayer
的所有屬性(可動畫的屬性)做動畫,但是UIView
把它關聯(lián)的圖層的這個特性關閉了。為了更好說明這一點,我們需要知道隱式動畫是如何實現(xiàn)的。
我們把改變屬性時CALayer
自動應用的動畫稱作行為,當CALayer
的屬性被修改時候,它會調(diào)用-actionForKey:
方法,傳遞屬性的名稱。剩下的操作都在CALayer
的頭文件中有詳細的說明,實質上是如下幾步:
CALayerDelegate
協(xié)議指定的-actionForLayer:forKey
方法。如果有,直接調(diào)用并返回結果。-actionForLayer:forKey
方法,圖層接著檢查包含屬性名稱對應行為映射的actions
字典。actions字典
沒有包含對應的屬性,那么圖層接著在它的style
字典接著搜索屬性名。style
里面也找不到對應的行為,那么圖層將會直接調(diào)用定義了每個屬性的標準行為的-defaultActionForKey:
方法。所以一輪完整的搜索結束之后,-actionForKey:
要么返回空(這種情況下將不會有動畫發(fā)生),要么是CAAction
協(xié)議對應的對象,最后CALayer
拿這個結果去對先前和當前的值做動畫。
于是這就解釋了UIKit是如何禁用隱式動畫的:每個UIView
對它關聯(lián)的圖層都扮演了一個委托,并且提供了-actionForLayer:forKey
的實現(xiàn)方法。當不在一個動畫塊的實現(xiàn)中,UIView
對所有圖層行為返回nil
,但是在動畫block范圍之內(nèi),它就返回了一個非空值。我們可以用一個demo做個簡單的實驗(清單7.5)
清單7.5 測試UIView的actionForLayer:forKey:
實現(xiàn)
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//test layer action when outside of animation block
NSLog(@"Outside: %@", [self.layerView actionForLayer:self.layerView.layer forKey:@"backgroundColor"]);
//begin animation block
[UIView beginAnimations:nil context:nil];
//test layer action when inside of animation block
NSLog(@"Inside: %@", [self.layerView actionForLayer:self.layerView.layer forKey:@"backgroundColor"]);
//end animation block
[UIView commitAnimations];
}
@end
運行程序,控制臺顯示結果如下:
$ LayerTest[21215:c07] Outside: <null>
$ LayerTest[21215:c07] Inside: <CABasicAnimation: 0x757f090>
于是我們可以預言,當屬性在動畫塊之外發(fā)生改變,UIView
直接通過返回nil
來禁用隱式動畫。但如果在動畫塊范圍之內(nèi),根據(jù)動畫具體類型返回相應的屬性,在這個例子就是CABasicAnimation
(第八章“顯式動畫”將會提到)。
當然返回nil
并不是禁用隱式動畫唯一的辦法,CATransacition
有個方法叫做+setDisableActions:
,可以用來對所有屬性打開或者關閉隱式動畫。如果在清單7.2的[CATransaction begin]
之后添加下面的代碼,同樣也會阻止動畫的發(fā)生:
[CATransaction setDisableActions:YES];
總結一下,我們知道了如下幾點
UIView
關聯(lián)的圖層禁用了隱式動畫,對這種圖層做動畫的唯一辦法就是使用UIView
的動畫函數(shù)(而不是依賴CATransaction
),或者繼承UIView
,并覆蓋-actionForLayer:forKey:
方法,或者直接創(chuàng)建一個顯式動畫(具體細節(jié)見第八章)。-actionForLayer:forKey:
委托方法,或者提供一個actions
字典來控制隱式動畫。我們來對顏色漸變的例子使用一個不同的行為,通過給colorLayer
設置一個自定義的actions
字典。我們也可以使用委托來實現(xiàn),但是actions
字典可以寫更少的代碼。那么到底改如何創(chuàng)建一個合適的行為對象呢?
行為通常是一個被Core Animation隱式調(diào)用的顯式動畫對象。這里我們使用的是一個實現(xiàn)了CATransaction
的實例,叫做推進過渡。
第八章中將會詳細解釋過渡,不過對于現(xiàn)在,知道CATransition
響應CAAction
協(xié)議,并且可以當做一個圖層行為就足夠了。結果很贊,不論在什么時候改變背景顏色,新的色塊都是從左側滑入,而不是默認的漸變效果。
清單7.6 實現(xiàn)自定義行為
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;
@property (nonatomic, weak) IBOutlet CALayer *colorLayer;/*熱心人發(fā)現(xiàn)這里應該改為@property (nonatomic, strong) 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
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: