UIView动画中View-Layer协作(学习)

前言

每个view都是由一个底层的layer来驱动的,也可以理解成view是layer的delegate(委托),当单独的layer属性发生变化时,都会触发一个从旧值过渡到新值的一个简单动画;如果是view中的layer,只是从这一帧直接编导下一帧,默认的隐式动画的layer行为就不起作用了。但是在通常情况下,view的layer某个属性在block外面被修改并不会触发动画,在block animation中却会触发动画,这是为什么?我在网络上查找资料看到的解释是,UIView默认情况下禁止了layer动画,而在block中又启用了它们,下面就开始简单介绍一些UIView动画的触发的过程,也权当我在学习过程中做一下学习笔记。

 

(一)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>

很明显,在block外面询问对应属性的action返回的是<null>,而在block内的话返回的是一个_UIViewAdditiveAnimationAction对象,这就解释了上面的理论。

 

(二)下面就是说一下UIView的block动画是一个怎样的执行流程,然后我写了一个很简单的block动画,然后通过这个动画,我记录了一下分析得出来的内容;(注,如果设置的属性的值前后没有改变,是不会触发动画的,这估计是苹果在内部做了逻辑判断;例如

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];
},

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

转载于:https://my.oschina.net/u/1450995/blog/686602

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值