CALayer的屬性行為其實很不正常,因為改變一個圖層的屬性并沒有立刻生效,而是通過一段時間漸變更新余指。這是怎么做到的呢醉途?
當你改變一個圖層的屬性矾瑰,屬性值的確是立刻更新的(如果你讀取它的數(shù)據(jù),你會發(fā)現(xiàn)它的值在你設(shè)置它的那一刻就已經(jīng)生效了)隘擎,但是屏幕上并沒有馬上發(fā)生改變殴穴。這是因為你設(shè)置的屬性并沒有直接調(diào)整圖層的外觀,相反货葬,他只是定義了圖層動畫結(jié)束之后將要變化的外觀采幌。
當設(shè)置CALayer的屬性,實際上是在定義當前事務(wù)結(jié)束之后圖層如何顯示的模型震桶。Core Animation扮演了一個控制器的角色休傍,并且負責根據(jù)圖層行為和事務(wù)設(shè)置去不斷更新視圖的這些屬性在屏幕上的狀態(tài)。
我們討論的就是一個典型的微型MVC模式蹲姐。CALayer是一個連接用戶界面(就是MVC中的view)虛構(gòu)的類磨取,但是在界面本身這個場景下,CALayer的行為更像是存儲了視圖如何顯示和動畫的數(shù)據(jù)模型柴墩。實際上忙厌,在蘋果自己的文檔中,圖層樹通常都是值的圖層樹模型江咳。
在iOS中逢净,屏幕每秒鐘重繪60次。如果動畫時長比60分之一秒要長歼指,Core Animation就需要在設(shè)置一次新值和新值生效之間爹土,對屏幕上的圖層進行重新組織。這意味著CALayer除了“真實”值(就是你設(shè)置的值)之外东臀,必須要知道當前顯示在屏幕上的屬性值的記錄着饥。
每個圖層屬性的顯示值都被存儲在一個叫做呈現(xiàn)圖層的獨立圖層當中,他可以通過-presentationLayer方法來訪問惰赋。這個呈現(xiàn)圖層實際上是模型圖層的復(fù)制宰掉,但是它的屬性值代表了在任何指定時刻當前外觀效果。換句話說赁濒,你可以通過呈現(xiàn)圖層的值來獲取當前屏幕上真正顯示出來的值(圖1)轨奄。
除了圖層樹,另外還有呈現(xiàn)樹拒炎。呈現(xiàn)樹通過圖層樹中所有圖層的呈現(xiàn)圖層所形成挪拟。注意呈現(xiàn)圖層僅僅當圖層首次被提交(就是首次第一次在屏幕上顯示)的時候創(chuàng)建,所以在那之前調(diào)用-presentationLayer將會返回nil击你。
你可能注意到有一個叫做–modelLayer的方法玉组。在呈現(xiàn)圖層上調(diào)用–modelLayer將會返回它正在呈現(xiàn)所依賴的CALayer谎柄。通常在一個圖層上調(diào)用-modelLayer會返回–self(實際上我們已經(jīng)創(chuàng)建的原始圖層就是一種數(shù)據(jù)模型)。

圖1 一個移動的圖層是如何通過數(shù)據(jù)模型呈現(xiàn)的
大多數(shù)情況下惯雳,你不需要直接訪問呈現(xiàn)圖層朝巫,你可以通過和模型圖層的交互,來讓Core Animation更新顯示石景。兩種情況下呈現(xiàn)圖層會變得很有用劈猿,一個是同步動畫,一個是處理用戶交互潮孽。
如果你在實現(xiàn)一個基于定時器的動畫揪荣,而不僅僅是基于事務(wù)的動畫,這個時候準確地知道在某一時刻圖層顯示在什么位置就會對正確擺放圖層很有用了往史。
如果你想讓你做動畫的圖層響應(yīng)用戶輸入仗颈,你可以使用 -hitTest: 方法來判斷指定圖層是否被觸摸,這時候?qū)Τ尸F(xiàn)圖層而不是模型圖層調(diào)用-hitTest:會顯得更有意義怠堪,因為呈現(xiàn)圖層代表了用戶當前看到的圖層位置揽乱,而不是當前動畫結(jié)束之后的位置。
我們可以用一個簡單的案例來證明后者(見清單1)粟矿。在這個例子中凰棉,點擊屏幕上的任意位置將會讓圖層平移到那里。點擊圖層本身可以隨機改變它的顏色陌粹。我們通過對呈現(xiàn)圖層調(diào)用-hitTest:來判斷是否被點擊撒犀。
如果修改代碼讓 -hitTest: 直接作用于colorLayer而不是呈現(xiàn)圖層,你會發(fā)現(xiàn)當圖層移動的時候它并不能正確顯示掏秩。這時候你就需要點擊圖層將要移動到的位置而不是圖層本身來響應(yīng)點擊(這就是為什么用呈現(xiàn)圖層來響應(yīng)交互的原因)或舞。
清單1, 使用presentationLayer圖層來判斷當前圖層位置
@interface MainCALayerVC ()
@property(nonatomic,strong)CALayer*layerOne;
@property(nonatomic,strong)CALayer*colorLayer;
@end
@implementation MainCALayerVC
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor=[UIColor orangeColor];
self.layerOne=[CALayer layer];
self.layerOne.frame=CGRectMake(0, 0, 40, 40);
self.layerOne.position=CGPointMake(30, NavHeight+30);
self.layerOne.backgroundColor=[UIColor greenColor].CGColor;
[self.view.layer addSublayer:self.layerOne];
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 {
CGPoint point = [[touches anyObject] locationInView:self.view];
if ([self.colorLayer.presentationLayer hitTest:point]) {
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:bluealpha:1.0].CGColor;
}else if ([self.layerOne.modelLayer hitTest:point])//或者[self.layerOne hitTest:point]
{
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.layerOne.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
}else {
[CATransaction begin];
[CATransaction setAnimationDuration:4.0];
self.colorLayer.position = point;
CGPoint point1=point;
point1.x=point.x+100;
self.layerOne.position=point1;
[CATransaction commit];
}
}
可以觀察到:在移動的過程中,若點擊self.colorLayer時蒙幻,顏色會立馬變化映凳。而點擊self.layerOne時,則不會變化邮破,不移動的時候才變化诈豌。
至此,你了解了呈現(xiàn)和模型圖層抒和,以及Core Animation是如何通過它們來判斷出圖層當前位置以及將要到達的位置矫渔。