Core Animation 是 Apple 的低级动画框架,在 CALayer 级别工作,确保内容在 GPU 上渲染。Core Animation 是 iOS 和 macOS 中所有动画的基础:UIKit、AppKit、SceneKit 和 SpriteKit 都构建在其之上。本文解释了 CALayer、CABasicAnimation、CATransaction 的架构和实用技术。
要点
Core Animation 是 Apple 的一个图形框架,出现在 Mac OS X 10.5 Leopard(2007)中,并从 iPhone OS(2007)的第一个版本移植到 iOS。Core Animation 管理图层(CALayer)在 GPU 上的合成、动画和渲染。iOS 中的每个 UIView 都有一个内置的 CALayer(view.layer),所有渲染都通过它进行。
Core Animation 基于隐式动画原理工作:CALayer 属性的一些更改会自动动画化(为 position、bounds、opacity、backgroundColor 等属性创建 CABasicAnimation)。隐式动画默认使用 CATransaction,持续时间为 0.25 秒,使用 easeInOut 曲线。
根据 Apple WWDC 2024 的数据,Core Animation 在配备 ProMotion 的设备上处理高达 120 FPS,使用 Metal 进行 GPU 渲染。内存中典型图层的大小为每像素 4 字节(RGBA)+ 每个图层约 100 字节的开销。建议每个屏幕不超过 1000 个图层以保持稳定的 60 FPS。
CALayer 是 Core Animation 的基础。每个 CALayer 包含位图内容(或 GPU 纹理引用)并定义几何形状(bounds、position、anchorPoint、transform、cornerRadius、borderWidth、shadow)。图层不处理触摸事件——这是 UIView/UIScrollView 的职责。图层形成层级结构(图层树),镜像视图层级。
Core Animation 支持三个图层树:模型图层树(实际属性)、呈现图层树(动画期间的当前显示)和渲染树(GPU 数据)。呈现图层用于实时读取动画的中间值。
CALayer 有专门的子类:CAShapeLayer(矢量图形)、CAGradientLayer(渐变)、CATextLayer(文本)、CATiledLayer(分割成瓦片的大图像)、CAEAGLLayer/MetalLayer(OpenGL/Metal)。CAShapeLayer 由于支持 CGPath 和 strokeStart/strokeEnd 动画,是最流行的自定义动画选择。
CABasicAnimation 是 CAAnimation 的子类,将 CALayer 的单个属性从初始值(fromValue)动画到最终值(toValue)或相对更改(byValue)。动画通过 layer.add(animation, forKey:) 方法添加到图层,并在渲染服务器中执行,无需应用程序在每一帧参与。
键(keyPath)是指向属性的字符串:opacity、position、bounds、transform.rotation.z、cornerRadius、shadowOpacity 等。Core Animation 支持 60 多个可动画化的 keyPath。对于自定义属性,使用 CATransaction 和呈现图层。
import QuartzCore
// CABasicAnimation 用于 opacity 和 cornerRadius
func animateLayer() {
let layer = CALayer()
layer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
layer.backgroundColor = UIColor.systemBlue.cgColor
view.layer.addSublayer(layer)
// 1. 透明度动画
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. 圆角动画
let cornerAnim = CABasicAnimation(keyPath: "cornerRadius")
cornerAnim.fromValue = 0
cornerAnim.toValue = 50
cornerAnim.duration = 2.0
cornerAnim.autoreverses = true
cornerAnim.repeatCount = .infinity
layer.add(fadeAnim, forKey: "fade")
layer.add(cornerAnim, forKey: "corner")
}
// 使用 ease-in-out 的 position 动画
func moveLayer(_ layer: CALayer, to point: CGPoint) {
let anim = CABasicAnimation(keyPath: "position")
anim.fromValue = NSValue(cgPoint: layer.position)
anim.toValue = NSValue(cgPoint: point)
anim.duration = 0.6
anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
anim.isRemovedOnCompletion = false
anim.fillMode = .forwards
layer.add(anim, forKey: "move")
layer.position = point // 更新模型图层
}重要:CABasicAnimation 是临时显示(呈现图层)。动画完成后,图层返回到模型图层的初始值。要固定最终状态,请设置 isRemovedOnCompletion = false、fillMode = .forwards,并将模型图层更新为最终值。
CATransaction 是一种将 Core Animation 动画分组到单个事务中的机制。CATransaction.begin() 和 CATransaction.commit() 定义事务边界。在事务内部,可以设置公共参数:duration、timingFunction、completionBlock、disableActions。CATransaction 是 CALayer 隐式动画的基础。
隐式动画在 CALayer 属性在动画块外更改时起作用:layer.opacity = 0.5。Core Animation 使用当前 CATransaction 的 duration(默认 0.25 秒)自动创建 CABasicAnimation。要禁用隐式动画,请使用 CATransaction.setDisableActions(true)。
// CATransaction — 分组和完成
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// 事务内的所有更改都以 1.5 的持续时间进行动画
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// 禁用隐式动画
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // 无动画
CATransaction.commit()
}
// CAShapeLayer 属性的动画
func animateProgress(percentage: CGFloat) {
CATransaction.begin()
CATransaction.setAnimationDuration(0.8)
CATransaction.setCompletionBlock {
print("Progress updated to (percentage)%")
}
progressLayer.strokeEnd = percentage / 100.0
CATransaction.commit()
}Core Animation 通过 UIView 图层与 UIKit 集成。下面的示例演示了 CAShapeLayer 的动画——一个带有弧长动画和通过 CABasicAnimation 旋转图层的圆形进度条。
import UIKit
import QuartzCore
class CircularProgressView: UIView {
private let progressLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(
arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
radius: bounds.width / 2 - 10,
startAngle: -.pi / 2,
endAngle: 3 * .pi / 2,
clockwise: true
)
progressLayer.path = path.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.lineWidth = 8
progressLayer.fillColor = nil
progressLayer.strokeEnd = 0
progressLayer.lineCap = .round
layer.addSublayer(progressLayer)
}
func setProgress(_ value: CGFloat, animated: Bool = true) {
if animated {
CATransaction.begin()
CATransaction.setAnimationDuration(0.6)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
progressLayer.strokeEnd = min(max(value, 0), 1)
CATransaction.commit()
} else {
progressLayer.strokeEnd = value
}
}
}CAShapeLayer 将 strokeEnd 属性从 0 动画到 1,创建圆形进度条的「填充」效果。CATransaction 以 0.6 的持续时间和 easeOut 使动画流畅。layer.lineCap = .round 为线条添加圆形末端,使外观更整洁。
常见问题
Core Animation 在 CALayer 级别工作,通过 CABasicAnimation、CATransaction 进行管理。UIView.animate 是 Core Animation 之上的高级封装,自动为 UIView 属性创建 CABasicAnimation。Core Animation 提供更多控制:keyPath、timingFunction、fillMode、通过 CATransaction 分组。
隐式动画是在动画块外更改 CALayer 可动画属性时自动进行的动画。例如,layer.opacity = 0.5 创建持续时间为 0.25 秒的 CABasicAnimation。隐式动画通过 CATransaction 管理,并通过 setDisableActions(true) 禁用。
对于自定义属性,在 CALayer 中实现 action(forKey:) 并返回 CAAnimation。或者使用 CALayer.display() 通过 CADisplayLink 进行手动渲染和动画。对于支持 CABasicAnimation 的属性,只需将 keyPath 指定为字符串即可。
CALayer 比 UIView 更轻量:没有事件处理程序(触摸、手势),不参与 Auto Layout,不支持 accessibility。典型 CALayer 占用约 100 字节,而 UIView 占用 300 多个字节。Core Animation 在 GPU 上的渲染服务器中处理图层,不会阻塞主线程。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。