前言網絡
每一個view都是由一個底層的layer來驅動的,也能夠理解成view是layer的delegate(委託),當單獨的layer屬性發生變化時,都會觸發一個從舊值過渡到新值的一個簡單動畫;若是是view中的layer,只是從這一幀直接編導下一幀,默認的隱式動畫的layer行爲就不起做用了。可是在一般狀況下,view的layer某個屬性在block外面被修改並不會觸發動畫,在block animation中卻會觸發動畫,這是爲何?我在網絡上查找資料看到的解釋是,UIView默認狀況下禁止了layer動畫,而在block中又啓用了它們,下面就開始簡單介紹一些UIView動畫的觸發的過程,也權當我在學習過程當中作一下學習筆記。ide
(一)layer經過向它的delegate發送actionForLayer:forKey來詢問對應屬性的action,若是是返回一個對象就執行這個對象的操做;返回nil或者是null都不會執行;這個就能夠解釋了上面描述的爲何view的layer某個屬性在block外面被修改並不會觸發動畫,在block animation中卻會觸發動畫;下面看一個段代碼:學習
NSLog(@"outside animation block: %@", [testView actionForLayer:testView.layer forKey:@"position"]); [UIView animateWithDuration:0.3 animations:^{ NSLog(@"inside animation block: %@", [testView actionForLayer:testView.layer forKey:@"position"]); }];
輸出的結果:動畫
2016-06-03 13:54:06.425 H5Test[1700:147380] outside animation block: <null>
2016-06-03 13:54:06.426 H5Test[1700:147380] inside animation block: <_UIViewAdditiveAnimationAction: 0x7fff4bc22dd0>debug
很明顯,在block外面詢問對應屬性的action返回的是<null>,而在block內的話返回的是一個_UIViewAdditiveAnimationAction對象,這就解釋了上面的理論。3d
(二)下面就是說一下UIView的block動畫是一個怎樣的執行流程,而後我寫了一個很簡單的block動畫,而後經過這個動畫,我記錄了一下分析得出來的內容;(注,若是設置的屬性的值先後沒有改變,是不會觸發動畫的,這估計是蘋果在內部作了邏輯判斷;例如code
testView.layer.opacity = 1;
[UIView animateWithDuration:5 animations:^{
testView.layer.opacity = 1;
} completion:^(BOOL finished) {
if (finished) {
NSLog(@"completion");
}
}];是不會觸發動畫的)。對象
#import "DRInspectionView.h" #import "DRInspectionLayer.h" @implementation DRInspectionView + (Class)layerClass { return [DRInspectionLayer class]; } - (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event { NSLog(@"event -- %@",event); return [super actionForLayer:layer forKey:event]; } @end DRInspectionView *testView = [[DRInspectionView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; testView.backgroundColor = [UIColor orangeColor]; // CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:@"opacity"]; // fadeIn.duration = 1; // fadeIn.fromValue = @0; // fadeIn.fillMode = kCAFillModeBoth; // fadeIn.toValue = @0; // fadeIn.delegate = [DRAnimationBlockDelegate animationDelegateWithBeginning:^{ // // NSLog(@"animation start"); // // } completion:^(BOOL finished) { // if (finished) { // // NSLog(@"animation completion"); // } // }]; //testView.layer.opacity = 1.0; //更改 model 的值 ... // ...而後添加動畫對象 //[testView.layer addAnimation:fadeIn forKey:@"fade in slowly"]; testView.layer.opacity = 0; [UIView animateWithDuration:5 animations:^{ testView.layer.opacity = 1; } completion:^(BOOL finished) { if (finished) { NSLog(@"completion"); } }]; [self.view addSubview:testView];
(1)執行animations,檢測改變了layer的opacity屬性,而後執行- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
NSLog(@"event -- %@",event);
return [super actionForLayer:layer forKey:event];
},返回執行動畫的對象;事件
(2)而後爲layer添加動畫,執行- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
{
NSLog(@"adding animation: %@", [anim debugDescription]);
[super addAnimation:anim forKey:key];
},ip
(3)最後會執行第(2)步添加的動畫,看看輸出的內容信息:
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- bounds
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- opaque
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- position
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- backgroundColor
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- opaque
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- opacity
2016-06-03 14:07:14.092 H5Test[1748:154159] event -- opacity
2016-06-03 14:10:30.829 H5Test[1748:154159] adding animation: <CABasicAnimation:0x7ff599443d10; delegate = <UIViewAnimationState: 0x7ff5994396f0>; fillMode = both; timingFunction = easeInEaseOut; duration = 5; fromValue = 0.0; keyPath = opacity>
2016-06-03 14:10:30.830 H5Test[1748:154159] event -- onOrderIn
2016-06-03 14:10:35.842 H5Test[1748:154159] completion
先是經過- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event一直檢測view的layer的全部屬性,當檢測到opacity屬性發生變化時返回了一個動畫對象,並執行了- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key添加了動畫事件,最後執行。
(三)研究block動畫執行
(1)block動畫的執行過程:先執行了animations block中的內容,發現opacity屬性值被修改。而後觸發view中的actionForLayer:forKey方法並返回動畫對象,最後執行view中layer的隱式動畫。經過了解了動畫執行的部份內部執行的原理,能更好的去使用block動畫,也會發現UIView的封裝動畫不會那麼神祕了。固然,研究得深刻的話,咱們還能夠實現本身的一套基於block的動畫APIs,說到這裏,其實咱們使用動畫的時候只能delegate委託回調,用慣了block的人仍是習慣於block回調,用起來比較方便。
(2)動畫的delegate的UIViewAnimationState內部私有類實現了- (void)animationDidStart:(CAAnimation *)anim以及- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag方法,這個內部的私用類的也有一個delegate也是一個私有類UIViewAnimationBlockDelegate負責執行delegate回調,所以咱們能夠修改一下,改爲block回調;
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface ZJAnimationBlockDelegate : NSObject @property (copy) void(^start)(void); @property (copy) void(^stop)(BOOL); + (instancetype)animationDelegateWithBeginning:(void(^)(void))beginning completion:(void(^)(BOOL finished))completion; @end #import "ZJAnimationBlockDelegate.h" @implementation ZJAnimationBlockDelegate + (instancetype)animationDelegateWithBeginning:(void (^)(void))beginning completion:(void (^)(BOOL))completion { ZJAnimationBlockDelegate *result = [ZJAnimationBlockDelegate new]; result.start = beginning; result.stop = completion; return result; } - (void)animationDidStart:(CAAnimation *)anim { if (self.start) { self.start(); } self.start = nil; } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if (self.stop) { self.stop(flag); } self.stop = nil; } @end //使用block回調; CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:@"opacity"]; fadeIn.duration = 1; fadeIn.fromValue = @0; fadeIn.fillMode = kCAFillModeBoth; fadeIn.toValue = @0; fadeIn.delegate = [ZJAnimationBlockDelegate animationDelegateWithBeginning:^{ NSLog(@"animation start"); } completion:^(BOOL finished) { if (finished) { NSLog(@"animation completion"); } }];
這裏自定義了一個委託類,委託類定義並實現了start,stop兩個回調block,裏面實現了- (void)animationDidStart:(CAAnimation *)anim以及- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag兩個方法,在方法中分別執行block回調,這一步就至關於替代了UIViewAnimationBlockDelegate的回調操做,實現動畫block回調。