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 是带着一对尖括号的 ("<null>"),这和其他对象一样,而打印 nil 的时候我们得到的是普通括号((null)):

outside animation block: <null>
inside animation block: <CABasicAnimation: 0x8c2ff10>

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

节选自Objc

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

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 10,528评论 5 13
  • 1 CALayer IOS SDK详解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi阅读 10,580评论 3 23
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,805评论 1 32
  • 前言 本文只要描述了iOS中的Core Animation(核心动画:隐式动画、显示动画)、贝塞尔曲线、UIVie...
    GitHubPorter阅读 8,999评论 7 11
  • 一 关于CoreAnimation CoreAnimation是苹果提供的一套基于绘图的动画框架,下图是官方文档中...
    路飞_Luck阅读 10,576评论 3 23