MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Swift Core Animation动画编程

2022-10-316.5k 阅读

Swift Core Animation 基础概念

Core Animation 是 iOS 和 macOS 开发中强大的动画框架,它能够实现各种流畅且绚丽的动画效果。在 Swift 中使用 Core Animation,可以为应用程序添加交互性和视觉吸引力。

Core Animation 的核心是图层(CALayer)。CALayer 类是 Core Animation 的基础,几乎所有的动画效果都与它相关。在 iOS 中,UIView 实际上是对 CALayer 的一个封装,UIView 负责处理用户交互,而 CALayer 负责呈现和动画。

例如,创建一个简单的 CALayer

import UIKit
import QuartzCore

let layer = CALayer()
layer.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
layer.backgroundColor = UIColor.red.cgColor
// 将该图层添加到某个视图的图层上,假设存在一个 view
view.layer.addSublayer(layer)

上述代码创建了一个红色的矩形 CALayer 并添加到了指定视图的图层上。

动画类型

隐式动画

在 Core Animation 中,对 CALayer 的某些属性进行更改时,会自动触发隐式动画。例如,改变 CALayerposition 属性:

// 假设已经有一个 layer 并添加到视图上
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    layer.position.x += 100
}

上述代码在两秒后,将 layerpositionx 坐标增加 100,这个过程会自动触发一个平滑的动画,这就是隐式动画。

隐式动画的时长等属性可以通过 CATransaction 来进行控制。例如,修改隐式动画的时长:

CATransaction.begin()
CATransaction.setAnimationDuration(5)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    layer.position.x += 100
}
CATransaction.commit()

这里通过 CATransaction 将隐式动画的时长设置为了 5 秒。

显式动画

显式动画允许开发者更精细地控制动画。主要使用 CAAnimation 及其子类来创建显式动画。

CABasicAnimation 是最常用的显式动画类之一,用于创建简单的基础动画。例如,创建一个位置移动的 CABasicAnimation

let animation = CABasicAnimation(keyPath: "position")
animation.toValue = CGPoint(x: layer.position.x + 200, y: layer.position.y)
animation.duration = 3
layer.add(animation, forKey: "positionAnimation")

上述代码创建了一个 CABasicAnimation,将 layer 在 3 秒内从当前位置移动到 x 坐标增加 200 的新位置。

CAKeyframeAnimation 用于创建关键帧动画,可以定义多个关键值,动画会按照这些关键值依次进行。例如,创建一个沿着特定路径移动的关键帧动画:

let path = UIBezierPath()
path.move(to: CGPoint(x: layer.position.x, y: layer.position.y))
path.addLine(to: CGPoint(x: layer.position.x + 100, y: layer.position.y + 100))
path.addLine(to: CGPoint(x: layer.position.x, y: layer.position.y + 200))

let keyframeAnimation = CAKeyframeAnimation(keyPath: "position")
keyframeAnimation.path = path.cgPath
keyframeAnimation.duration = 5
layer.add(keyframeAnimation, forKey: "keyframePositionAnimation")

此代码创建了一个 CAKeyframeAnimationlayer 会沿着定义的 UIBezierPath 在 5 秒内移动。

CATransition 用于创建过渡动画,例如视图切换动画。以下是一个简单的淡入过渡动画示例:

let transition = CATransition()
transition.type = .fade
transition.duration = 1
// 假设要在 view 上进行过渡动画
view.layer.add(transition, forKey: "fadeTransition")

上述代码在 view 的图层上添加了一个时长为 1 秒的淡入过渡动画。

动画的组合与控制

动画组

可以使用 CAAnimationGroup 来组合多个动画,让它们同时播放。例如,同时进行位置移动和透明度变化的动画:

let positionAnimation = CABasicAnimation(keyPath: "position")
positionAnimation.toValue = CGPoint(x: layer.position.x + 100, y: layer.position.y)
positionAnimation.duration = 3

let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.toValue = 0.5
opacityAnimation.duration = 3

let animationGroup = CAAnimationGroup()
animationGroup.animations = [positionAnimation, opacityAnimation]
animationGroup.duration = 3
layer.add(animationGroup, forKey: "groupAnimation")

上述代码创建了一个 CAAnimationGroup,包含位置移动和透明度变化两个动画,它们会同时在 3 秒内执行。

动画的暂停与恢复

可以通过操作 layertimeOffsetspeed 属性来暂停和恢复动画。暂停动画:

let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
layer.speed = 0
layer.timeOffset = pausedTime

恢复动画:

let pausedTime = layer.timeOffset
layer.speed = 1
layer.timeOffset = 0
layer.beginTime = CACurrentMediaTime() - pausedTime

上述代码实现了对 layer 上动画的暂停和恢复操作。

动画的高级特性

弹簧动画

在 iOS 10 及以上,可以使用 CASpringAnimation 来创建弹簧动画,为动画添加真实物理效果。例如,创建一个弹簧动画来移动 layer

let springAnimation = CASpringAnimation(keyPath: "position")
springAnimation.toValue = CGPoint(x: layer.position.x + 100, y: layer.position.y)
springAnimation.mass = 1
springAnimation.stiffness = 100
springAnimation.damping = 10
springAnimation.initialVelocity = 0
springAnimation.duration = springAnimation.settlingDuration
layer.add(springAnimation, forKey: "springPositionAnimation")

上述代码创建了一个弹簧动画,通过调整 mass(质量)、stiffness(刚度)、damping(阻尼)等属性来控制弹簧的物理效果。

3D 动画

Core Animation 支持 3D 动画,可以通过设置 layertransform 属性来实现。例如,创建一个 3D 旋转动画:

let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.y")
rotationAnimation.toValue = Double.pi
rotationAnimation.duration = 2
layer.add(rotationAnimation, forKey: "3DRotationAnimation")

上述代码创建了一个围绕 y 轴旋转 180 度的 3D 旋转动画,时长为 2 秒。

与 UIKit 的结合

虽然 Core Animation 可以直接在 CALayer 上操作,但在 iOS 开发中,通常会与 UIView 结合使用。UIView 提供了一些方便的动画方法,底层其实也是基于 Core Animation。

例如,使用 UIView.animate 方法创建一个简单的视图淡入动画:

UIView.animate(withDuration: 1) {
    view.alpha = 0.5
}

这是一个简洁的淡入动画,UIView.animate 方法会自动管理动画的开始、结束以及相关的过渡效果。

同时,也可以在 UIView 上添加基于 CALayer 的动画。例如,在 UIViewlayer 上添加一个 CABasicAnimation

let view = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
let animation = CABasicAnimation(keyPath: "position")
animation.toValue = CGPoint(x: view.layer.position.x + 100, y: view.layer.position.y)
animation.duration = 3
view.layer.add(animation, forKey: "viewPositionAnimation")

上述代码在 UIViewlayer 上添加了一个位置移动的 CABasicAnimation

性能优化

在使用 Core Animation 进行动画编程时,性能优化是很重要的。以下是一些优化建议:

  1. 减少重绘:尽量避免在动画过程中频繁改变会导致重绘的属性,如 backgroundColor。如果必须改变,可以考虑使用 CABasicAnimation 来动画 backgroundColor,而不是直接设置新值。
  2. 使用离屏渲染:对于复杂的动画,可以启用离屏渲染,通过设置 layershouldRasterize 属性为 true。但要注意,离屏渲染会占用额外的内存,所以要谨慎使用。
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
  1. 优化动画时长和帧率:选择合适的动画时长和帧率,避免过长或过短的时长以及过高或过低的帧率。一般来说,60fps 是 iOS 设备的理想帧率,动画时长根据具体需求合理设置,避免动画过长导致用户等待或过短导致用户无法察觉。

  2. 缓存动画:对于重复使用的动画,可以进行缓存。例如,将常用的 CAAnimation 对象存储在一个数组或字典中,需要时直接取出使用,而不是每次都重新创建。

实战案例:创建一个动态菜单

假设要创建一个类似侧边栏的动态菜单,点击按钮时,菜单从屏幕左侧滑出,再次点击按钮时,菜单滑回。

  1. 创建界面: 在视图控制器中添加一个按钮和一个视图作为菜单。
class ViewController: UIViewController {
    
    let menuView = UIView()
    let button = UIButton(type: .system)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        menuView.frame = CGRect(x: -200, y: 0, width: 200, height: view.bounds.height)
        menuView.backgroundColor = UIColor.gray
        view.addSubview(menuView)
        
        button.frame = CGRect(x: 20, y: 20, width: 100, height: 40)
        button.setTitle("Toggle Menu", for: .normal)
        button.addTarget(self, action: #selector(toggleMenu), for: .touchUpInside)
        view.addSubview(button)
    }
    
    @objc func toggleMenu() {
        // 动画逻辑在这里实现
    }
}
  1. 添加动画: 在 toggleMenu 方法中添加动画逻辑,使用 CABasicAnimation 来实现菜单的滑入滑出效果。
@objc func toggleMenu() {
    let animation = CABasicAnimation(keyPath: "position.x")
    if menuView.layer.position.x < 0 {
        animation.toValue = menuView.frame.width / 2
    } else {
        animation.toValue = -menuView.frame.width / 2
    }
    animation.duration = 0.3
    animation.fillMode = .forwards
    animation.isRemovedOnCompletion = false
    menuView.layer.add(animation, forKey: "menuSlideAnimation")
    
    // 同时更新实际的位置,以确保交互正常
    if menuView.layer.position.x < 0 {
        menuView.layer.position.x = menuView.frame.width / 2
    } else {
        menuView.layer.position.x = -menuView.frame.width / 2
    }
}

上述代码实现了一个简单的动态菜单,通过点击按钮,菜单会平滑地滑入或滑出屏幕。在动画过程中,设置了 fillMode.forwards 以及 isRemovedOnCompletionfalse,以确保动画结束后保持在最终状态。同时,手动更新了 menuView 的实际位置,以保证用户交互的正确性。

总结

通过上述内容,我们深入了解了 Swift 中 Core Animation 的各个方面,从基础概念到各种动画类型,再到动画的组合、控制以及与 UIKit 的结合和性能优化,最后通过实战案例进一步巩固了知识。Core Animation 为开发者提供了强大的工具来创建富有交互性和视觉吸引力的应用程序,熟练掌握它能够极大提升应用的用户体验。在实际开发中,需要根据具体需求选择合适的动画方式,并注意性能优化,以打造出流畅且高效的动画效果。

以上是关于 Swift Core Animation 动画编程的详细介绍,希望对您在使用 Core Animation 进行开发时有所帮助。在不断实践中,您会更加熟练地运用这些知识,创造出更出色的动画效果。