前言
上一章中我們深入研究了UIView和它持有的那個CALayer之間的關(guān)系乌昔,知道了我們對UIView的各種屬性的操作實際上都是間接的操作了CALayer對應(yīng)的屬性取刃。 這一章中我們將進一步探究iOS動畫竹捉,看看UIView是如何將CoreAnimation封裝成block動畫的炸客。
CALayer的可動畫屬性
CALayer擁有大量的屬性几于,如果大家按住cmd點進CALayer的頭文件中看的話话侧,會發(fā)現(xiàn)很多的屬性的注釋中,最后會有一個詞叫做Animatable益老,直譯過來是可動畫的彪蓬。
如果一個屬性被標記為Animatable,那么它具有以下兩個特點:
1捺萌、直接對它賦值可能產(chǎn)生隱式動畫档冬;
2、我們的CAAnimation的keyPath可以設(shè)置為這個屬性的名字桃纯。
當我們直接對可動畫屬性賦值的時候酷誓,由于有隱式動畫存在的可能,CALayer首先會判斷此時有沒有隱式動畫被觸發(fā)态坦。它會讓它的delegate(沒錯CALayer擁有一個屬性叫做delegate)調(diào)用actionForLayer:forKey:來獲取一個返回值盐数,這個返回值在聲明的時候是一個id對象,當然在運行時它可能是任何對象驮配。這時CALayer拿到返回值娘扩,將進行判斷:如果返回的對象是一個nil,則進行默認的隱式動畫壮锻;如果返回的對象是一個[NSNull null] 琐旁,則CALayer不會做任何動畫;如果是一個正確的實現(xiàn)了CAAction協(xié)議的對象猜绣,則CALayer用這個對象來生成一個CAAnimation灰殴,并加到自己身上進行動畫。
UIView的block動畫
思考一個這樣的問題:為什么同樣的一行代碼在block里面就有動畫在block外面就沒動畫掰邢,就像下面這樣:
// 這樣寫沒有動畫
view.center = CGPointMake(80, 80);
[UIView animateWithDuration:1.25 animations:^{
// 寫在block里面就有動畫
view.center = CGPointMake(80, 80);
}];
我們分別在block外面和block里面打印actionForLayer:forKey:方法的返回值
注意:
如果你的代碼大概是這樣的
view.center = CGPointMake(40, 40);
[UIView animateWithDuration:2.0 animations:^{
view.center = CGPointMake(40, 40);
} completion:^(BOOL finished) {
NSLog(@"aaa");
}];
aaa不會被打印牺陶,completion里面的block將會瞬間被調(diào)用而不是2秒之后調(diào)用,因為你這樣寫辣之,沒有動畫產(chǎn)生掰伸,也就是動畫一開始就結(jié)束了。
同樣
[UIView animateWithDuration:2.0 animations:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
view.center = CGPointMake(80, 80);
});
} completion:^(BOOL finished) {
NSLog(@"bbb");
}];
completionBlock會馬上執(zhí)行怀估。
動畫是怎樣被加到CALayer上的
在TestAnimationView中重寫方法:
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
id<CAAction> obj = [super actionForLayer:layer forKey:event];
NSLog(@"%@",obj);
return obj;
}
然后在TestAnimationLayer中重寫方法:
- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
{
[super addAnimation:anim forKey:key];
NSLog(@"%@",[anim debugDescription]);
}
在ViewController中調(diào)用一下:
- (void)viewDidLoad {
[super viewDidLoad];
TestAnimationView * view = [[TestAnimationView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.view addSubview:view];
[UIView animateWithDuration:1.25 animations:^{
view.center = CGPointMake(80, 80);
} completion:^(BOOL finished) {
NSLog(@"aaa");
}];
}
調(diào)用NSLog(@”%@”,[anim debugDescription]);后我們會打印出這樣的內(nèi)容:
沒錯狮鸭,UIView將CAAction返回給layer后,layer使用這個對象生成了一個CABasicAnimation并且調(diào)用了[self addAnimation..]多搀。通過這個CABasicAnimation對象的信息我們可以知道很多東西歧蕉。
首先是additive這個屬性為true。在iOS8 之前康铭,UIKit創(chuàng)建的動畫默認是不使用additive的惯退,而在iOS8之后,默認是Additive的从藤。Additive 的動畫采用的是相對值設(shè)計催跪,添加到 presentationLayer 的動畫的變化范圍是:modelLayerValue + fromValue -> modelLayerValue + toValue锁蠕。參考:Additve
//這個屬性確定動畫執(zhí)行的狀態(tài)是否疊加在控件的原狀態(tài)上
//默認設(shè)置為NO,如果我們執(zhí)行兩次位置移動的動畫叠荠,會從同一位置執(zhí)行兩次
//如果設(shè)置為YES匿沛,則會在第一次執(zhí)行的基礎(chǔ)上執(zhí)行第二次動畫
接下來是delegate扫责,CAAnimation將在動畫結(jié)束后回調(diào)它delegate的animationDidStop方法榛鼎,我們發(fā)現(xiàn)這個delegate又是一個私有類,因為我們在調(diào)用UIView動畫的時候設(shè)置了completionBlock鳖孤,也就是動畫結(jié)束后要調(diào)用的block者娱,所以UIView會將這個私有delegate的信息放進CAAction對象中告知CALayer動畫結(jié)束后我要干事情(調(diào)用這個block)。
??關(guān)于fillMode和timingFunction我們將在探究動畫時間的時候來詳細對它進行講解苏揣。這里你會發(fā)現(xiàn)timingFunction默認的是easeInEaseOut黄鳍,也就是淡入淡出效果。
??keyPath被設(shè)置為了position平匈,那是因為我們在動畫block中是對center賦值框沟,對應(yīng)到layer中就是position了。