UIView動畫中View-Layer協做(學習)

前言網絡

每一個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回調。

相關文章
相關標籤/搜索