CAAnimation — 核心概念、CAAnimationGroup 和层动画

作者: IT Sectr 发布日期: 2026-03-01 阅读时间: 8 分钟

CAAnimation — Core Animation 的抽象基类,定义了所有动画类型的公共接口:CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup、CATransition。本文解释了类的层级结构、关键属性、动画分组以及 Swift 中的实践示例。

核心要点

  • CAAnimation — Core Animation 的抽象基类,CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup、CATransition 继承自它
  • CAAnimationGroup — 使用共同的 duration 和 timingFunction 并行执行多个动画
  • CAKeyframeAnimation — 使用值和时间戳数组进行关键帧动画
  • CAMediaTiming — 时间管理协议:duration、repeatCount、autoreverses、timeOffset
  • CATransition — 层状态之间的预定义过渡(fade、push、moveIn、reveal)

什么是 CAAnimation?

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 的值。

CAAnimation 的类层级结构

Core Animation 提供了四个主要的 CAAnimation 子类,每个针对自己的动画场景。了解层级结构有助于为任务选择正确的类型。

用途关键特点
CABasicAnimation将一个属性从 A 动画到 BfromValue, toValue, byValue, keyPath
CAKeyframeAnimation多个关键帧的动画values, keyTimes, path, calculationMode
CAAnimationGroup分组多个动画animations 数组,统一 duration
CATransition层状态之间的过渡type (fade/push/moveIn/reveal), subtype, startProgress

CABasicAnimation — 最简单、最常用的子类。CAKeyframeAnimation 用于复杂的轨迹(例如沿贝塞尔曲线运动)。CAAnimationGroup 并行启动多个动画。CATransition — 图像或内容状态之间的现成过渡。

CAMediaTiming — 时间管理

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 和 CAKeyframeAnimation

CAAnimationGroup 将多个 CAAnimation 组合成一个分组。如果没有为每个设置 beginTime,所有子动画将并行启动。分组有自己的 duration,限制子动画的最大持续时间。如果子动画未设置自己的 timingFunction,则应用分组的 timingFunction 作为默认值。

CAKeyframeAnimation 通过由 values 数组定义的多个关键帧对属性进行动画。参数 keyTimes(从 0 到 1 的 NSNumber 数组)确定每个帧的时间戳。calculationMode 指定插值方法:linear、discrete(无插值)、paced(均匀速度)、cubic(样条)、cubicPaced。

swift
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。使用 pathcalculationMode = .cubicPaced 的 CAKeyframeAnimation 提供沿贝塞尔曲线的均匀运动,带有自动旋转(rotationMode = .auto)。bounceAnimation 使用 isAdditive 进行相对位移。

Swift 代码示例

完整的 CAAnimation 示例,使用 CATransition 进行内容切换和 CAAnimationGroup 进行脉冲动画。

swift
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 和 CAKeyframeAnimation 之间有什么区别?

CABasicAnimation 使用均匀插值将属性从初始值(fromValue)动画到最终值(toValue)。CAKeyframeAnimation 允许为非线性帧动画指定中间值(values)和时间戳(keyTimes)数组,包括通过 path 沿曲线运动。

什么时候使用 CAAnimationGroup?

CAAnimationGroup 在多个动画需要并行执行并保证同步时使用:分组的 duration 限制所有子动画。将分组用于组合效果:同时缩放 + 透明度变化 + 旋转。

CAAnimation 中的 fillMode 是什么?

fillMode 确定动画前(backwards)和动画后(forwards)层的显示。值 .forwards 在动画结束后将层保持在最终状态(需要 isRemovedOnCompletion = false)。.both 组合 backwards(开始前的初始状态)和 forwards。

如何区分 CAAnimation 和 CALayer 动画?

CAAnimation 是一个动画对象(指令),通过 layer.add(animation, forKey:) 添加到 CALayer。动画作为独立对象存在,可以重复使用(通过拷贝)并通过 CAMediaTiming 管理。一个层可以拥有多个不同键的动画。

总结

  • CAAnimation — 实现 CAMediaTiming 和 CAMediaTimingFillMode 的 Core Animation 抽象基类
  • 类层级结构 — CABasicAnimation(单个属性)、CAKeyframeAnimation(多个帧)、CAAnimationGroup(分组)、CATransition(过渡)
  • CAMediaTiming — 带有 duration、repeatCount、autoreverses、beginTime、timeOffset、speed、fillMode 的协议
  • CAAnimationGroup — 使用统一 duration 和 timingFunction 并行执行动画
  • CAKeyframeAnimation — 使用 calculationMode 通过 values/keyTimes 或 path(贝塞尔曲线)动画
  • CATransition — 预定义类型:fade、push、moveIn、reveal,可配置 subtype
  • 管理 — layer.add(animation, forKey:)、removeAnimation(forKey:)、removeAllAnimations()

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读