前言网络
每一个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回调。