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