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回调。

相关文章
相关标签/搜索