Core Animation文档阅读笔记

这篇文章并不是用来demo在iOS开发中如何利用Core Animation框架完成动画的。而是,想要尝试探究Core Animation一些原理和容易被忽略的东西。

UIView动画

对于iOS编程的初学者来说,在使用动画的时候,使用UIView本身的block-based animation就能够满足绝大部分的业务需求了。但是如果有下面三个场景,使用Core Animation提供的动画类来设计完成动画就更加方便、灵活:

  • 想要更加细粒度地控制动画参数(比如,动画中视图位移不是直线路径);
  • 想要创建动画组,包含多个同时进行的动画;

另外,对于UIView本身的block-based animation而言,其本质也是在相应的CALayer上完成动画。这一点与直接创建CAAnimation的子类动画对象(CABasicAnimation, CAKeyFrameAnimation),配置动画参数,添加到对应layer上并没有太多实质的区别。所以可以将UIView本身的block-based animation看成是一层Wrapper或者是Apple文档中提到的implicit animation(隐式动画)。

在之后看完WWDC的Core Animation Essentials这一个session之后,里面Apple提出并且定义了所谓的implicit animation:就是当开发人员创建一个独立layer(非UIView自身自带的default layer,而是开发者instantiate的一个添加到某个layer上的CALayer实例),当直接改变layer的某些可动画属性时,layer会以动画的形式来表现这种改变。这种implicit animation正式相对于显示地explicit创建一个CAAnimation的对象然后添加到某个layer上而产生的动画而言的。

CALayer

UIView实际上是在CALayer之上的一层thin wrapper。至少从Apple的文档看来,性能已经很好了;所以,也更加推荐尽量使用UIView及其子类来完成App视图层级的构建。另外,UIView本身还支持Autolayout,所以就更加值得使用了。

CALayer本身,它是如果将我们动态的App内容渲染出来的。文档也做了一些说明。简单的讲,也就是如果我们是使用UIView,渲染的过程是自动的(也就是我们并没有显式地赋值layer的contents属性);而如果我们需要使用独立的layer,那就需要我们显式地给该layer指定内容。当然,需要区别是的这个内容,并不是指layer的背景颜色,边框。下图就能较好说明layer的内容是那个部分。

layer内容结构

也就是最终一个layer不管是静态地展示还是在动画过程中。它都是通过将自己的内容和其他附加属性合成为一张bitmap,绘制到屏幕上。

CALayer在开放的API中有两种不同的作用的layer(这里谈论的不是layer的子类有多少):model layerpresentation layermodel layer中存储了在非动画过程中,我们直接观察到的layer的状态(背景色、opacity等);而presentation layer是对动画过程变化图层的一种近似。也就是说我们在(比如,移动view的过程中)是能够通过presentation layer来获取到动画过程中某一时刻的view的位置。

Implicit Animation Vs. Explicit Animation

Core Animation的文档中多次提到这两个术语:implicit animation和explicit animation,但并没有做出专门的解答。

我的理解是,但凡没有显示地(explicitly)创建一个CAAnimation的子类动画对象,同时添加到layer上进行的动画而产生的动画效果都可以被叫做隐式动画。对于目前这个部分文档的理解,包括了两种类型:

  • UIView本身的block-based animation;
  • 单独被添加到一个UIView对象上的layer,在改变了一个可动画属性之后产生的动画;

关于第二点需要做一些说明。一个独立的layer在被添加到一个已经有了一个默认layer的UIView对象上(该类视图被叫做layer-backed view);如果改变这个独立layer的可动画属性,那么这个属性的改变是自动会被动画的。想要禁止这个动画的出现需要使用CATransaction的方法。

[CATransaction begin];
[CATransaction setDisableActions:YES]; //如果去掉,就会看到动画效果
animationLayer.backgroundColor = [UIColor blueColor].CGColor; //独立layer
[CATransaction commit];

那这个改变了backgroundColor就在独立layer产生的动画就是前面提到的implicit animation。需要禁用这种动画就需要显示地开始一个CATransaction,在该事务中禁用掉所有的actions

但是,这样为什么禁用掉了动画??

所有的CAAnimation都遵守CAAction这一类的协议。CAAction实际定义了更广义的CALayer的行为。一个layer上的动画,都是可以看成是一种具体实现CAAction的协议方法的行为。所以,当你禁用layer上的所有actions之后,当然动画效果也不会出现了。

对于前面提到的view-backed layer(比如所有的默认的UIView类及其子类创建的对象),它们的默认layer在修改可动画属性时,都是没有动画的。具体实现和禁用独立layer的动画实现方式可能类似。然后在UIView的动画block中,再开启所有的actions。

CATransaction

前面我们看到了CATransaction被用来禁用动画。CATransaction的实际用途还有很多。在Apple文档中提到:当你改变layer的属性时,如果没有显示开启一个事务,那么Core Animation会自动地开启一个事务。

CATransaction的用途比较广泛,比如:

  • 在一个事务中,可以修改layer的多个属性,做统一的一次事务提交;
  • 事务可以嵌套nested transactions,它可以保证在不同的动画组中有不同的动画参数,互不影响。
      [CATransaction begin]; // Outer transaction
      // Change the animation duration to two seconds
      [CATransaction setValue:[NSNumber numberWithFloat:2.0f] forKey:kCATransactionAnimationDuration];
      // Move the layer to a new position
      theLayer.position = CGPointMake(0.0,0.0);
      [CATransaction begin]; // Inner transaction
      // Change the animation duration to five seconds
      [CATransaction setValue:[NSNumber numberWithFloat:5.0f]
      		forKey:kCATransactionAnimationDuration];
      // Change the zPosition and opacity
      theLayer.zPosition=200.0;
      theLayer.opacity=0.0;
      [CATransaction commit]; // Inner transaction
      [CATransaction commit]; // Outer transaction
    
  • 可以利用事务修改隐式动画的参数。
      [CATransaction begin];
      [CATransaction setValue:[NSNumber numberWithFloat:10.0f] forKey:kCATransactionAnimationDuration];
      // Perform the animations
      [CATransaction commit];
    

参考文献