iOS CoreAnimation 隐式动画

1,805 阅读3分钟

#前言

文章之前先问个小题目吧:

如下图,点击屏幕时,红色View的center修改为点击的位置,红色View移动过程是否有动画?如果有,动画时间是多少?

redView.png

如果你是个老司机,能回答这道题,并深知其底层原理,那就别浪费时间了,赶快离开吧。 文章最后给出答案,想好了翻到文章最后查看答案。

##1、要弄清楚这个小题目,就需要研究CoreAnimation的底层原理了 Core Animation通常对CALayer的可动画属性做动画,如果将题中的View换成CALayer,将会有动画。CALayer 属于 QuartzCore 框架, UIView 属于 UIKit 框架。UIKit建立在Core Animation之上,隐式动画如何被 UIKit 禁用掉的呢?

1、修改layer的动画属性时,会调用CALayer的一个方法,搜索一个动作

/* Returns the action object associated with the event named by the
 * string 'event'. The default implementation searches for an action
 * object in the following places:
 *
 * 1. if defined, call the delegate method -actionForLayer:forKey:
 * 2. look in the layer's `actions' dictionary
 * 3. look in any `actions' dictionaries in the `style' hierarchy
 * 4. call +defaultActionForKey: on the layer's class
 *
 * If any of these steps results in a non-nil action object, the
 * following steps are ignored. If the final result is an instance of
 * NSNull, it is converted to `nil'. */

- (nullable id<CAAction>)actionForKey:(NSString *)event;

2、如果这个方法返回一个实现了CAAction协议的对象,返回的对象将会调用CAAction协议的一个方法

/* Called to trigger the event named 'path' on the receiver. The object
 * (e.g. the layer) on which the event happened is 'anObject'. The
 * arguments dictionary may be nil, if non-nil it carries parameters
 * associated with the event. */

- (void)runActionForKey:(NSString *)event object:(id)anObject
    arguments:(nullable NSDictionary *)dict;

3、上面方法中 anObject 是个 CALayer,anObject 调用
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;方法,animation会拷贝一份添加到CALayer上,用于产生动画。

#2动作搜索 上面第一步,搜索动作的过程比较复杂, - (nullable id<CAAction>)actionForKey:(NSString *)event;方法搜索动作的步骤是:

1、检测 CALayer 的 delegate ,看看delegate是否实现 -actionForLayer:forKey: 方法,如果有delegate,并且实现了此方法,那就直接调用并返回结果。 2、如果没有delegate,或没有实现 -actionForLayer:forKey: 方法,就检测属性名对应动作的 actions 字典。 3、如果 actions 字典没有对应的属性,就搜索 style 字典 4、如果 style 字典也没有,就调用+ (nullable id)defaultActionForKey:(NSString *)event 方法。

以上任何一步,如果返回非 nil,则搜索停止,否则继续往下搜索。 经过一轮搜索之后,- actionForKey:(NSString *);要么返回nil 不会有动画,要么是CAAction协议的对象,CALayer 拿这个对象做动画。但有一种特殊情况,就是返回 [NSNull null] ,由于是非 nil,则搜索停止, [NSNull null] 也会转化为 nil,导致没有动画的结果。

UIView中,layer的delegate指向UIView,UIView的-actionForLayer:forKey:方法默认返回 [NSNull null],在Block动画中返回 CAAnimation 对象,所以UIView的隐式动画才能被禁止。可以通过下面的代码来证明:

    NSLog(@"%@   %@", nil ,[NSNull null]);
    NSLog(@"%@", [self.redView actionForLayer:self.redView.layer forKey:@"backgroundColor"]);
    

    [UIView animateWithDuration:0.1 animations:^{
        NSLog(@"%@", [self.redView actionForLayer:self.redView.layer forKey:@"backgroundColor"]);
    }];

打印结果:

(null) <CABasicAnimation: 0x60800003b8c0> 注意: [NSNull null] 的打印结果是尖括号


文章开头问题答案

最后回答文章开头的问题:没有动画,UIView禁用了隐式动画,具体原因和原理查看文章吧。

实验 demo