#前言
文章之前先问个小题目吧:
如下图,点击屏幕时,红色View的center修改为点击的位置,红色View移动过程是否有动画?如果有,动画时间是多少?
如果你是个老司机,能回答这道题,并深知其底层原理,那就别浪费时间了,赶快离开吧。 文章最后给出答案,想好了翻到文章最后查看答案。
##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] 的打印结果是尖括号