为什么 View 的根 Layer 没有隐式动画?

上一篇整理了 关于隐式动画的笔记

我们知道根 layer 是不能做隐式动画的
就像这样, 是没有动画效果的, 颜色瞬间就变化了

    self.view.layer.backgroundColor = [UIColor blueColor].CGColor;

那么隐式动画是如何被禁用掉呢?
我们知道 Core Animation 通常对 CALayer 的所有可动画的属性做动画
但是 UIView 把它的根 layer 这个特性关闭了
为了更好说明这一点,我们需要知道隐式动画是如何实现的。

我们把改变属性时 CALayer 自动应用的动画称作行为
当 CALayer 的属性被修改时候,它会调用 -actionForKey: 方法,传递属性的名称
-actionForKey: 经过一波猛虎操作
流程如下

  • 图层首先检测它是否有委托,并且是否实现 CALayerDelegate 协议指定的 -actionForLayer:forKey 方法
    如果有,直接调用并返回结果。
  • 如果没有委托,或者委托没有实现 -actionForLayer:forKey 方法
    图层接着检查包含属性名称对应行为映射的 actions 字典。
  • 如果 actions 字典没有包含对应的属性,那么图层接着在它的 style 字典接着搜索属性名
  • 最后,如果在 style 里面也找不到对应的行为,那么图层将会直接调用定义了每个属性的标准行为的-defaultActionForKey:方法。

所以一轮完整的搜索结束之后,-actionForKey: 要么返回空(这种情况下将不会有动画发生)
要么是 CAAction 协议对应的对象,最后 CALayer 拿这个结果去对先前和当前的值做动画

于是这就解释了 UIView 是如何禁用根 Layer 隐式动画的:
每个 UIView 都成为了根 layer 的 delegate,并且提供了 -actionForLayer:forKey 的实现方法
当属性的改变不在一个事务中的时候返回值为空, 这种情况下将不会有动画发生
当属性的改变在一个事务中的时候返回值非空, 这时候就会有动画

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"Outside: %@", [self.layerView actionForLayer:self.layerView.layer forKey:@"backgroundColor"]);
    // 开启一个事务
    [UIView beginAnimations:nil context:nil];
    NSLog(@"Inside: %@", [self.layerView actionForLayer:self.layerView.layer forKey:@"backgroundColor"]);
    [UIView commitAnimations];
}

打印结果

$ LayerTest[21215:c07] Outside: <null>
$ LayerTest[21215:c07] Inside: <CABasicAnimation: 0x757f090>

于是我们知道
当属性在事务之外发生改变
UIView 直接通过返回nil来禁用隐式动画
但如果在事务范围之内
根据动画具体类型返回相应的属性
当然返回 nil 并不是禁用隐式动画唯一的办法
CATransacition 有个方法叫做 +setDisableActions:
可以用来对所有属性打开或者关闭隐式动画。如果在 [CATransaction begin] 之后添加下面的代码
同样也会阻止动画的发生:

[CATransaction setDisableActions:YES];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。