Skip to content

iOS 开发 - 动画

动画是现代用户界面的关键组成部分,通过提供视觉反馈和引导注意力来增强用户体验。在 iOS 中,动画范围广泛,从简单的视图属性变化到复杂的交互式序列。创建逐帧动画的一种基本方法是快速显示一系列图像。

创建帧动画项目——“精灵动画”

Section titled “创建帧动画项目——“精灵动画””

本项目演示了一个简单的帧动画。我们将在点击按钮时,在 UIImageView 中显示一系列图像,模拟动画角色或效果。您需要一组构成动画序列的图像(GIF 的帧或精灵图)。在本示例中,假设您有命名为 frame_0.png、frame_1.png、…、frame_8.png 的图像。

要获取这些图像,您可以搜索精灵图(例如,“走路动画精灵图”)或 GIF,然后使用在线转换器或图像编辑软件将其分割成单个帧。确保图像按顺序命名。

项目设置:

  1. 在 Xcode 中创建一个新的 iOS 项目(App 模板,Storyboard 界面,Swift 语言)。
  2. 将您的动画帧图像序列(例如 frame_0.png 到 frame_8.png)添加到项目的资源目录(Asset Catalog,即 Assets.xcassets)中。将它们拖到资源列表中。

界面 (Main.storyboard):

  1. 将一个 UIImageView 拖到主视图控制器的视图上。它将显示动画帧。
  2. 将一个 UIButton 拖到视图上。将其标题设置为“切换动画”或类似文本。

UI 将包含一个图像视图(最初为空或显示第一帧)和一个用于启动/停止动画的按钮。

连接 UI 到代码 (ViewController.swift):

并排打开 Main.storyboard 和 ViewController.swift 文件。

  • 从 UIImageView 按住 Control 键拖动到 ViewController.swift 中,创建一个 @IBOutlet。将其命名为 animationImageView。
  • 从 UIButton 按住 Control 键拖动到 ViewController.swift 中,为“Touch Up Inside”事件创建一个 @IBAction。将其命名为 toggleAnimationTapped。

在 ViewController.swift 中设置动画属性:

class ViewController: UIViewController {
@IBOutlet weak var animationImageView: UIImageView!
var animationFrames: [UIImage] = []
var currentFrameIndex = 0
var animationTimer: Timer?
var isAnimating = false
let animationDurationPerFrame: TimeInterval = 0.1 // 每秒10帧
let totalFrames = 9 // 假设图像名为 frame_0.png 到 frame_8.png
override func viewDidLoad() {
super.viewDidLoad()
loadAnimationFrames()
// 可选地初始显示第一帧
if !animationFrames.isEmpty {
animationImageView.image = animationFrames[0]
}
}
func loadAnimationFrames() {
for i in 0..<totalFrames {
if let imageName = "frame_\(i)", let image = UIImage(named: imageName) {
animationFrames.append(image)
}
}
}
// ... 代码的其余部分 ...
}

loadAnimationFrames 函数从您的资源目录中获取 UIImage 对象并填充到数组中。名称 frame_0、frame_1 等应与您在 Asset Catalog 中使用的名称匹配。

实现动画逻辑:

@objc func updateAnimationFrame() {
currentFrameIndex += 1
if currentFrameIndex >= animationFrames.count {
currentFrameIndex = 0 // 循环播放动画
}
if !animationFrames.isEmpty {
animationImageView.image = animationFrames[currentFrameIndex]
}
}
@IBAction func toggleAnimationTapped(_ sender: UIButton) {
if isAnimating {
// 停止动画
animationTimer?.invalidate() // 停止计时器
animationTimer = nil
sender.setTitle("Start Animation", for: .normal)
} else {
// 启动动画
if animationFrames.isEmpty { return } // 如果没有帧则不启动
currentFrameIndex = 0 // 重置到第一帧
animationImageView.image = animationFrames[currentFrameIndex]
animationTimer = Timer.scheduledTimer(timeInterval: animationDurationPerFrame,
target: self,
selector: #selector(updateAnimationFrame),
userInfo: nil,
repeats: true)
sender.setTitle("Stop Animation", for: .normal)
}
isAnimating.toggle()
}

解释:

  • updateAnimationFrame(): 此方法由 Timer 调用。它前进到下一帧并更新 animationImageView。当到达帧序列的末尾时,它会循环回到开头。
  • toggleAnimationTapped(_:): 此动作方法处理按钮点击。
    • 如果 isAnimating 为 true,它会使 Timer 失效(停止)并更新按钮标题为“Start Animation”。
    • 如果 isAnimating 为 false,它会启动一个新的 Timer。Timer.scheduledTimer 会按照 animationDurationPerFrame 指定的时间间隔重复调用 updateAnimationFrame。按钮标题更新为“Stop Animation”。
  • #selector(updateAnimationFrame) 语法用于引用计时器的 updateAnimationFrame 方法。

运行应用: 构建并运行。图像视图可能会显示第一帧。点击“Start Animation”按钮。您应该看到图像视图循环显示帧,创建动画。点击“Stop Animation”暂停它。

使用 UIImageView.animationImages 的替代方法: UIKit 也提供了一种更简单的方法来实现基本帧动画:

// 在 toggleAnimationTapped 内部,另一种实现方式:
// if animationImageView.isAnimating {
// animationImageView.stopAnimating()
// sender.setTitle("Start Animation", for: .normal)
// } else {
// if animationImageView.animationImages == nil || animationImageView.animationImages?.isEmpty == true {
// animationImageView.animationImages = animationFrames
// animationImageView.animationDuration = TimeInterval(animationFrames.count) * animationDurationPerFrame
// // animationImageView.animationRepeatCount = 0 // 0 表示无限循环
// }
// animationImageView.startAnimating()
// sender.setTitle("Stop Animation", for: .normal)
// }
// isAnimating.toggle() // 您仍然需要这个状态变量或检查 animationImageView.isAnimating

这个替代方法使用了 UIImageView 内置的动画属性(animationImages、animationDuration、startAnimating()、stopAnimating())。对于基本序列来说,它通常更简单。如果您需要在帧之间同步其他事件或执行动作,Timer 方法提供了更多的控制。

挑战:修改动画使其只播放一次而不是循环播放,或者添加控制动画速度的控件。