隐式动画

作者: SmallflyBlog | 来源:发表于2017-03-26 13:30 被阅读64次

一般我们在做动画的时,会使用到 CAAnimation 的相关类,通过 CALayer 的 addAnimation:forKey: 方法,添加动画效果,这种动画称为显式动画。还有一种动画被称为隐式的动画,在没有主动添加动画代码时,会自动的产生动画效果。

我们知道 UIView 的背后,有 CALayer 作为内容的显示,CALayer 类似于 UIView 也具有树型结构,可以单独的创建。单独的创建 CALayer 实例,并修改它的某些属性,会神奇的出现动画效果。

override func viewDidLoad() {
    super.viewDidLoad()   
    
    animationLayer = CALayer()
    animationLayer?.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    animationLayer?.backgroundColor = UIColor.red.cgColor
    view.layer.addSublayer(animationLayer!)
}
    
@IBAction func move() {
    let centerX: CGFloat = CGFloat(Int(arc4random()) % 375)
    let centerY: CGFloat = CGFloat(Int(arc4random()) % 667)
    let center = CGPoint(x: centerX, y: centerY)
    animationLayer?.position = center
}

上述代码使用 CALayer 创建一个红色的正方形,并随机的修改它的位置。效果是:

Moving.gif

修改 CALayer 的 position,它没有立即出现在目标位置,而会有过渡的动画效果,该过程为 0.25 秒,这就是所谓的隐式动画。

隐式动画的背后,存在事务的概念,任何一个 animatable 属性的修改,都会默认创建一个 CATransaction 的事务,来配置动画的参数。重写 CATransaction 可以覆盖默认的动画效果。

将动画时间修改为五秒:

CATransaction.begin()
CATransaction.setAnimationDuration(5)
animationLayer?.position = center
CATransaction.commit()

替换成上述的代码段,移动的效果变慢了。另外,还可以用 CATransaction.setDisableActions(true) 方法直接禁用隐式动画。需要注意的是,修改隐式动画效果的代码,都必须要在 begin()commit() 之间,并且成对出现。

CATransaction 没有实例方法,它像个神秘的配置工具类。当嵌套的使用 CATransaction 时,它以栈式结构管理。执行 begin 的时,会将后面的配置信息入栈;当的 commit 时,出栈配置信息,处于它们之间的属性修改是原子的,等下一次 RunLoop 到来时,开始执行动画。

举一个嵌套使用的例子:

CATransaction.begin()
CATransaction.setAnimationDuration(1)
let centerX: CGFloat = CGFloat(Int(arc4random()) % 375)
let centerY: CGFloat = CGFloat(Int(arc4random()) % 667)
let center = CGPoint(x: centerX, y: centerY)
animationLayer?.position = center
    
CATransaction.begin()
CATransaction.setAnimationDuration(5)
let random: CGFloat = CGFloat(Int(arc4random()) % 4)
let transform = CGAffineTransform(rotationAngle: CGFloat(M_PI_4) * random)
animationLayer?.setAffineTransform(transform)
CATransaction.commit()
        
CATransaction.commit()

外层是移动的效果,内层是旋转效果,内层的效果会先被提交执行。

仔细观察动画效果,肉眼发现旋转和移动几乎是同时开始的,它们分明是按顺序提交的两段代码,为什么会同时执行?

这时候就需要了解下 CALayer 动画的形成原理,在 CALayer 的头文件里面有两个方法,modelLayer()presentationLayer()它们分别叫模型树和呈现树,文档已有详细说明。

当我们开始动画的时候,我们必须要先确定动画最终效果是什么,在给某个动画属性赋新值的时候,它是瞬间被修改的,我们从模型树中读取,它的值已经是新的值,而在动画过程中的值由呈现树来保存。但实际上 CA 内部还有私有的渲染树 CARender,渲染当前的动画效果,并且是异步的,所以上面两次提交的动画,是分别在不同的线程中执行的,并且不会阻塞主线程。

UIView 是如何禁用隐式动画的

UIView 的内容是由 CALayer 呈现的,但在修改 UIView 属性的时候,却没有动画效果,说明 UIView 在包装 CALayer 的时,对它做了手脚。

CALayer 属性的修改的动画被叫做 action,只要实现 CAAction 协议的类,都可以被作用于 CALayer 的动画。当 CALayer 的属性被修改时,首先会调用 -actionForKey:方法,传递修改的属性,接着会通过4种方式来获取动画的 action:

  1. CALayer 是否存在代理,并实现 -actionForlayer:forKey 方法。
  2. 如果代理不存在或者未实现上述代理方法,会检查 actions 字典,是否包含所修改属性的动作。
  3. 如果 actions 字典,不包含当前修改的属性,会在继承体系中查找 style 字典属性。
  4. 最后如果在 style 属性里也没法发现,就会使用默认的 -defaultActionForKey:,也就是形成隐式动画的动作。

然而,UIView 禁用隐式动画的方式非常简单,它遵循了 CALayerDelegate 并给 方法返回 nil,就不会有动画效果了。

等等,那如果直接返回 nil,那 UIView 不就做不了动画了吗?animateWithDuration 这类 block 动画如何实现呢?比如修改颜色的动画效果:

 UIView.animate(withDuration: 1, delay: 10, options: .curveEaseIn, animations: { 
    self.view.backgroundColor = UIColor.black
}, completion: nil)

虽然传递 delay 10 秒,但 block 是瞬间执行的,只不过动画的效果会在 10 秒之后才发生。上述 block 等价于

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDelay(10)
UIView.setAnimationDuration(1)
self.view.backgroundColor = UIColor.black
UIView.commitAnimations()

处于 beginAnimationscommitAnimations 之间的属性修改会被做标记 ,在 -actionForlayer:forKey 方法中,会为有标记的操作返回特定的 CAAction 而不是 nil,UIView 就可以自如的控制是否需要动画效果了。

本文绝大部分内容 CALayer 头文件就有说明,但是如果仅仅看头文件的注释,会不知所云,稍微了解一些用法,会清楚不少。

相关文章

  • 取消(关闭)隐式动画

    取消(关闭)隐式动画** 可以通过动画事务(CATransaction)关闭默认的隐式动画效果[CATransac...

  • 隐式动画&显式动画 学习笔记

    隐式动画 CALayer的属性基本上都可以进行隐式动画 CATransaction可以控制隐式动画(执行时间或者关...

  • iOS-CALayer (四)

    上一篇 : iOS-CALayer (三) 前言:继续深入学习动画,主要从隐式动画、显式动画上车。 一、隐式动画 ...

  • iOS动画笔记

    在iOS各类动画效果中,习惯分为两类:隐式动画和显式动画。 隐式动画 简单的讲,由系统进行相关动画配置,执行动画效...

  • Core Animation小记(三)

    动画 1.隐式动画。没有制定任何动画的类型叫做隐式动画。事务,是通过CATransaction类来做管理,只能通过...

  • iOS之动画

    1.隐式动画 1.1.什么是隐式动画? 了解什么是隐式动画之前,要先了解是什么根层和非根层.根层:UIView内部...

  • iOS隐式动画与显式动画的区别

    请参考iOS隐式动画与显式动画的区别

  • SwiftUI -- View 动画

    SwiftUI 中的动画有两种类型:显式动画和隐式动画。 一、显式动画 显式动画通过 withAnimation ...

  • CoreAnimation

    隐式动画 所谓的隐式动画,之所以叫隐式是因为我们并没有指定任何动画的类型。我们仅仅改变了一个属性,然后CoreAn...

  • iOS动画-CAAnimation使用详解

    理解了隐式动画后,显式动画就更加通俗易懂了。区别于隐式动画的特点,显式动画就是需要我们明确指定类型、时间等参数来实...

网友评论

    本文标题:隐式动画

    本文链接:https://www.haomeiwen.com/subject/poqkottx.html