iOS中UIView与CALayer的关系


在 iOS 中,所有的 view 都是由一个底层的 layer 来驱动的。view 和它的 layer 之间有着紧密的联系,UIView继承自UIResponder, 能接收并响应事件, 负责显示内容的管理, 而CALayer继承自NSObject, 不能响应事件, 负责显示内容的绘制,view 其实直接从 layer 对象中获取了绝大多数它所需要的数据。在 iOS 中也有一些单独的 layer,比如 AVCaptureVideoPreviewLayer 和 CAShapeLayer,它们不需要附加到 view 上就可以在屏幕上显示内容。两种情况下其实都是 layer 在起决定作用。当然了,附加到 view 上的 layer 和单独的 layer 在行为上还是稍有不同的。

基本上你改变一个单独的 layer 的任何属性的时候,都会触发一个从旧的值过渡到新值的简单动画(这就是所谓的可动画 animatable)。然而,如果你改变的是 view 中 layer 的同一个属性,它只会从这一帧直接跳变到下一帧。尽管两种情况中都有 layer,但是当 layer 附加在 view 上时,它的默认的隐式动画的 layer 行为就不起作用了。

animatable;几乎所有的层的属性都是隐性可动画的。你可以在文档中看到它们的简介是以 'animatable' 结尾的。这不仅包括了比如位置,尺寸,颜色或者透明度这样的绝大多数的数值属性,甚至也囊括了像 isHidden 和 doubleSided 这样的布尔值。 像 paths 这样的属性也是 animatable 的,但是它不支持隐式动画。

在 Core Animation 编程指南的 “How to Animate Layer-Backed Views” 中,对为什么会这样做出了一个解释:

UIView 默认情况下禁止了 layer 动画,但是在 animation block 中又重新启用了它们

这正是我们所看到的行为;当一个属性在动画 block 之外被改变时,没有动画,但是当属性在动画 block 内被改变时,就带上了动画。对于这是如何发生的这一问题的答案十分简单和优雅,它优美地阐明和揭示了 view 和 layer 之间是如何协同工作和被精心设计的。

无论何时一个可动画的 layer 属性改变时,layer 都会寻找并运行合适的 'action' 来实行这个改变。在 Core Animation 的专业术语中就把这样的动画统称为动作 (action,或者 CAAction)。

CAAction:技术上来说,这是一个接口,并可以用来做各种事情。但是实际中,某种程度上你可以只把它理解为用来处理动画。

layer 将像文档中所写的的那样去寻找动作,整个过程分为五个步骤。第一步中的在 view 和 layer 中交互的部分是最有意思的:
layer 通过向它的 delegate 发送 actionForLayer:forKey: 消息来询问提供一个对应属性变化的 action。delegate 可以通过返回以下三者之一来进行响应:

1.它可以返回一个动作对象,这种情况下 layer 将使用这个动作。
2.它可以返回一个 nil, 这样 layer 就会到其他地方继续寻找。
3.它可以返回一个 NSNull 对象,告诉 layer 这里不需要执行一个动作,搜索也会就此停止。

而让这一切变得有趣的是,当 layer 在背后支持一个 view 的时候,view 就是它的 delegate;

在 iOS 中,如果 layer 与一个 UIView 对象关联时,这个属性必须被设置为持有这个 layer 的那个 view。

理解这些之后,前一分钟解释起来还复杂无比的现象瞬间就易如反掌了:属性改变时 layer 会向 view 请求一个动作,而一般情况下 view 将返回一个 NSNull,只有当属性改变发生在动画 block 中时,view 才会返回实际的动作。你可以非常容易地验证到底是不是这样。只要对一个一般来说可以动画的 layer 属性向 view 询问动作就可以了,比如对于 'position':

  NSLog(@"outside animation block: %@",
[myView actionForLayer:myView.layer forKey:@"position"]);

 [UIView animateWithDuration:0.3 animations:^{
      NSLog(@"inside animation block: %@",
      [myView actionForLayer:myView.layer forKey:@"position"]);
}];

运行上面的代码,可以看到在 block 外 view 返回的是 NSNull 对象,而在 block 中时返回的是一个 CABasicAnimation。很优雅,对吧?值得注意的是打印出的 NSNull 是带着一对尖括号的 (""),这和其他对象一样,而打印 nil 的时候我们得到的是普通括号((null)):

outside animation block:
inside animation block:

对于 view 中的 layer 来说,对动作的搜索只会到第一步为止(至少我没有见过 view 返回一个 nil 然后导致继续搜索动作的情况)。对于单独的 layer 来说,剩余的四个步骤可以在 CALayer 的 actionForKey: 文档中找到。

节选自Objc

你可能感兴趣的:(iOS中UIView与CALayer的关系)