CAAnimation — Core Animation 的抽象基类,定义了所有动画类型的公共接口:CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup、CATransition。本文解释了类的层级结构、关键属性、动画分组以及 Swift 中的实践示例。
核心要点
CAAnimation — 抽象的 Core Animation 类,定义了所有动画的公共接口。它实现了 CAMediaTiming 协议(时间、重复、自动返回)和 CAMediaTimingFillMode(动画前/后的行为)。CAAnimation 不能直接实例化 — 请使用以下子类之一:CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup、CATransition 或 CAAnimation(通过 animationForKeyPath)。
每个 CAAnimation 通过 add(_:forKey:) 方法添加到 CALayer。参数 forKey 是动画在层上的唯一标识符。同一个 keyPath 只能有一个活动动画。要替换动画,请在添加新动画前使用 removeAnimation(forKey:)。
根据 Apple 的观点,CAAnimation 是层的临时性变更。动画结束后,如果没有设置 fillMode 和 isRemovedOnCompletion,则 presentation layer 返回到 model layer 的值。
Core Animation 提供了四个主要的 CAAnimation 子类,每个针对自己的动画场景。了解层级结构有助于为任务选择正确的类型。
| 类 | 用途 | 关键特点 |
|---|---|---|
| CABasicAnimation | 将一个属性从 A 动画到 B | fromValue, toValue, byValue, keyPath |
| CAKeyframeAnimation | 多个关键帧的动画 | values, keyTimes, path, calculationMode |
| CAAnimationGroup | 分组多个动画 | animations 数组,统一 duration |
| CATransition | 层状态之间的过渡 | type (fade/push/moveIn/reveal), subtype, startProgress |
CABasicAnimation — 最简单、最常用的子类。CAKeyframeAnimation 用于复杂的轨迹(例如沿贝塞尔曲线运动)。CAAnimationGroup 并行启动多个动画。CATransition — 图像或内容状态之间的现成过渡。
CAMediaTiming 协议— CAAnimation 的基本组成部分。它定义了基本时间属性:duration(持续时间)、repeatCount(重复次数)、repeatDuration(重复总时长)、autoreverses(自动返回到起点)、beginTime(相对于父级的开始时间)、timeOffset(在一个循环内的偏移)、speed(播放速度)。
fillMode 属性(CAMediaTimingFillMode)确定动画前(backwards)和动画后(forwards)层的显示。值:removed(默认 — 返回到 model layer)、forwards(保持在最终状态)、backwards(在开始前显示初始状态)、both(组合两种行为)。fillMode 仅在 isRemovedOnCompletion = false 时生效。
CAAnimation 继承 CAMediaTiming,并可以嵌入到父动画(CAAnimationGroup)中。在这种情况下,beginTime 相对于父级的开始计算,而 duration 受到父级的限制。Speed = 2.0 将动画速度提高一倍。
CAAnimationGroup 将多个 CAAnimation 组合成一个分组。如果没有为每个设置 beginTime,所有子动画将并行启动。分组有自己的 duration,限制子动画的最大持续时间。如果子动画未设置自己的 timingFunction,则应用分组的 timingFunction 作为默认值。
CAKeyframeAnimation 通过由 values 数组定义的多个关键帧对属性进行动画。参数 keyTimes(从 0 到 1 的 NSNumber 数组)确定每个帧的时间戳。calculationMode 指定插值方法:linear、discrete(无插值)、paced(均匀速度)、cubic(样条)、cubicPaced。
import QuartzCore
// CAAnimationGroup — 动画组合
func complexLayerAnimation(_ layer: CALayer) {
let fade = CABasicAnimation(keyPath: "opacity")
fade.fromValue = 1.0
fade.toValue = 0.3
let scale = CABasicAnimation(keyPath: "transform.scale")
scale.fromValue = 1.0
scale.toValue = 1.5
let rotate = CABasicAnimation(keyPath: "transform.rotation.z")
rotate.fromValue = 0
rotate.toValue = Double.pi * 2
let group = CAAnimationGroup()
group.animations = [fade, scale, rotate]
group.duration = 2.0
group.repeatCount = .infinity
group.autoreverses = true
layer.add(group, forKey: "pulse")
}
// CAKeyframeAnimation — 沿轨迹运动
func animateAlongPath(_ layer: CALayer) {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addCurve(
to: CGPoint(x: 200, y: 300),
controlPoint1: CGPoint(x: 100, y: 0),
controlPoint2: CGPoint(x: 100, y: 300)
)
let anim = CAKeyframeAnimation(keyPath: "position")
anim.path = path.cgPath
anim.duration = 1.5
anim.calculationMode = .cubicPaced
anim.rotationMode = .auto
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
layer.add(anim, forKey: "pathAnim")
layer.position = CGPoint(x: 200, y: 300)
}
// CAKeyframeAnimation 使用自定义值
func bounceAnimation(_ layer: CALayer) {
let bounce = CAKeyframeAnimation(keyPath: "position.y")
bounce.values = [0, -50, 0, -25, 0, -10, 0]
bounce.keyTimes = [0, 0.15, 0.3, 0.45, 0.6, 0.8, 1.0]
bounce.duration = 0.8
bounce.timingFunction = CAMediaTimingFunction(name: .easeOut)
bounce.isAdditive = true // 相对于当前位置
layer.add(bounce, forKey: nil)
}CAAnimationGroup 以 duration 2.0 并行执行 fade、scale 和 rotate。使用 path 和 calculationMode = .cubicPaced 的 CAKeyframeAnimation 提供沿贝塞尔曲线的均匀运动,带有自动旋转(rotationMode = .auto)。bounceAnimation 使用 isAdditive 进行相对位移。
完整的 CAAnimation 示例,使用 CATransition 进行内容切换和 CAAnimationGroup 进行脉冲动画。
import UIKit
import QuartzCore
class AnimationDemoView: UIView {
private let demoLayer = CALayer()
private var currentColor: CGColor = UIColor.systemBlue.cgColor
override func layoutSubviews() {
super.layoutSubviews()
demoLayer.frame = CGRect(x: bounds.midX - 40, y: bounds.midY - 40,
width: 80, height: 80)
demoLayer.backgroundColor = currentColor
demoLayer.cornerRadius = 12
layer.addSublayer(demoLayer)
}
func animateTransition() {
// CATransition — 状态之间的过渡
let transition = CATransition()
transition.type = .fade
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
demoLayer.add(transition, forKey: nil)
// 通过事务更新内容
currentColor = currentColor == UIColor.systemBlue.cgColor
? UIColor.systemRed.cgColor
: UIColor.systemBlue.cgColor
demoLayer.backgroundColor = currentColor
}
func startPulse() {
let scaleUp = CABasicAnimation(keyPath: "transform.scale")
scaleUp.fromValue = 1.0
scaleUp.toValue = 1.3
let fadeOut = CABasicAnimation(keyPath: "opacity")
fadeOut.fromValue = 1.0
fadeOut.toValue = 0.6
let glow = CABasicAnimation(keyPath: "shadowOpacity")
glow.fromValue = 0.0
glow.toValue = 0.8
let group = CAAnimationGroup()
group.animations = [scaleUp, fadeOut, glow]
group.duration = 1.2
group.autoreverses = true
group.repeatCount = .infinity
group.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
demoLayer.add(group, forKey: "pulse")
}
func stopAnimations() {
demoLayer.removeAllAnimations()
demoLayer.transform = CATransform3DIdentity
demoLayer.opacity = 1.0
demoLayer.shadowOpacity = 0.0
}
}使用 type: .fade 的 CATransition 在两个 backgroundColor 状态之间创建流畅过渡。CAAnimationGroup 组合 scale、opacity 和 shadowOpacity 产生脉冲效果。removeAllAnimations() 重置所有动画并将层返回到 model layer 的原始值。
常见问题
CABasicAnimation 使用均匀插值将属性从初始值(fromValue)动画到最终值(toValue)。CAKeyframeAnimation 允许为非线性帧动画指定中间值(values)和时间戳(keyTimes)数组,包括通过 path 沿曲线运动。
CAAnimationGroup 在多个动画需要并行执行并保证同步时使用:分组的 duration 限制所有子动画。将分组用于组合效果:同时缩放 + 透明度变化 + 旋转。
fillMode 确定动画前(backwards)和动画后(forwards)层的显示。值 .forwards 在动画结束后将层保持在最终状态(需要 isRemovedOnCompletion = false)。.both 组合 backwards(开始前的初始状态)和 forwards。
CAAnimation 是一个动画对象(指令),通过 layer.add(animation, forKey:) 添加到 CALayer。动画作为独立对象存在,可以重复使用(通过拷贝)并通过 CAMediaTiming 管理。一个层可以拥有多个不同键的动画。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。