16 May 2017

View Animations vs Layer Animations

View Animations

Layer Animations

CABasicAnimation

动画不绑定到指定的 Layer,每次调用 add(anim: CAAnimation>, forKey: String?) 都会生成一份该动画的拷贝,并应用到当前 Layer 上。

动画本质上并不是改变 View 的真实状态,而只是改变 View 的一个缓存版本 Presentation Layer,当动画开始时,隐藏 View,当动画结束时,显示 View

  1. 如果当前视图所处的状态即动画的最终状态(如: 位置,透明度),则需要指定 fillMode = kCAFillModeBackwards 在动画被添加到 Layer 后,不管真实的开始时间是什么,动画的第一帧都会立即执行,从而保证不会先看到视图的最终状态,然后再执行动画。
  2. 如果设置视图的初始状态为动画的初始状态,在将动画添加到视图之后,需要设置视图的最终状态为动画的最终状态,以保证真实的视图状态发生变化。

当动画执行结束后,如果要保留动画的最后一帧在视图上,需要同时设置 isRemovedOnCompletion = falsefillMode = kCAFillModeForwardsfillMode = kCAFillModeBoth

由于上面提到动画的是 Presentation Layer,因此保留动画的最后一帧时,当前显示视图并不是真实的视图,因此失去交互性,无法进行如激活输入框等操作

CAAnimationDelegate

使用 UIKit 动画,你无法在创建动画之后,暂停、结束甚至访问它;而使用 Core Animation,你可以很容易的结束一个动画,甚至可以为动画设置一个委托,观察动画执行的状态。

可以通过设置 delegate 检测动画的开始和结束状态,由于动画可以被添加到多个 Layer,因此如果我们需要区分当前的 Layer,需要在动画被添加到 Layer 之前,通过如 setValue(layer, forKey: "layer") 的方式存储对当前 Layer 的引用,并在 delegate 对应的方法中获取引用。在动画执行的过程中无法对动画对象进行修改。

CAAnimationGroup

CAAnimationGroup 的属性设置会应用到组内的所有动画上, animations 是不可变的,需要一次即完全赋值。动画缓冲(Animation easing)包含缓入、缓出、缓入和缓出,如通过代码来实现缓入效果:

timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)

也可以通过 CAMediaTimingFunction(controlPoints...) 构造来自定义缓冲点。

缓入:由慢到快;缓出:由快到慢;缓入缓出:由慢到快再到慢;

More timing options

repeatCountautoreverse 共同使用时,由于使用 autoreverse 会导致视图回到起点状态,可以通过设置 repeatCount = 1.5 的方式来实现动画停止在最终位置。可以通过设置动画的 speed 属性控制动画的速度,也可以直接设置 Layerspeed 属性来控制速度,但是注意:最终速度取决于 Layer 层级和动画上速度值的乘积。

Layer Springs

使用支持弹性的 UIView.animate() 方法时,你需要传入 duration 参数,UIKit 负责根据传入的时长控制其它的参数值,因此有时候这种方式的弹性动画会略显突兀。而使用 Core AnimationCASpringAnimation 类,你可以指定关于弹性配置的所有参数 damping(默认 10.0)、mass(默认 1.0)、stiffness(默认 100.0)、initialVelocity(默认 0.0),最后将 settlingDuration(根据参数配置获取动画过程的时长) 值赋值给 duration

想象着你在推一个人荡秋千,initialVelocity(初始速度) 值越大会让秋千上的人往前荡的更远,mass(质量) 的值越大会让秋千向后荡的更远,stiffness(刚度) 的值越大越使得秋千来回荡的速度更快,damping(阻尼)越大秋千来回荡的次数也就越少。

Keyframe Animations

View keyframe animations 可以让你结合不同的动画,动画不同的视图属性,以及允许动画之间有重叠或者间隔。 Layer keyframe animations 只能让你动画单个 Layer 的单个属性,而且中间不能有重叠或间隔。

一个上下摇摆的动画:

let wobble = CAKeyframeAnimation(keyPath: "transform.rotation")
wobble.duration = 0.25
wobble.repeatCount = 4
wobble.values = [0.0, -M_PI_4/4, 0.0, M_PI_4/4, 0.0]
wobble.keyTimes = [0.0, 0.25, 0.5, 0.75, 1.0]
view.layer.add(wobble, forKey: nil)
Animating struct values

以下是动画一个 struct 类型的 CGPoint, 你也可以动画 CGSize, CGRect, CATransform3D

let flight = CAKeyframeAnimation(keyPath: "position")
flight.duration = 12.0
flight.values = [CGPoint(x: 0.0, y: 0.0),
					CGPoint(x: 100.0, y: 50),
					CGPoint(x: 0.0, y: 100)]
flight.keyTimes = [0.0, 0.5, 1.0]
layer.add(flight, forKey: nil)

Shapes and Masks

CAShapeLayer 可用于 Layer 的遮罩层(Mask),其 path 属性可使用 UIBezierPath 构造,且可动画该属性。

Gradient Animations

CAGradientLayer 使用 mask 属性,并动画其 locations 属性,实现 slide to unlock 效果。