跳到主要内容

Apple

概述

本指南介绍如何开始使用 Apple 运行时库。Rive 运行时库是开源的,源代码可在其 GitHub 仓库 中获取。

该库包含一个 API,供 Apple 应用在 UIKit/AppKit 和 SwiftUI 中轻松集成 Rive 资源。运行时可通过 CocoaPods 或 Swift Package Manager 安装。

Apple 运行时目前支持 iOS 14.0+、visionOS 1.0+、tvOS 16.0+、macOS 13.1+ 和 Mac Catalyst 14.0+。

新的 Apple 运行时设计为 Swift 优先的 API,利用 Swift Concurrency,并改进了 Rive 的多线程支持。

入口点是 Rive 类型,它是 Rive 视图配置的容器,包括文件、画板、状态机、适配方式和背景颜色。

添加 Rive 视图的方法是创建一个带有 Rive 对象的 RiveUIView,然后将其添加到视图层次结构中。在 SwiftUI 中,只需在 RiveUIView 对象上调用 .view() 即可。

需要注意的是,虽然支持多线程,但 Rive 对象的调用仍必须在主线程上进行。这通过将函数和类型标记为 @MainActor 在编译时强制执行。

大多数 API 都标记为 throws,并为不同的 Rive 原语提供了 Error 类型。

快速开始

按照以下步骤快速开始将 Rive 集成到你的 Apple 应用中。

当前运行时

安装运行时

随着 CocoaPods 进入维护模式,我们推荐使用 Swift Package Manager。

要通过 Xcode 安装,你可以按照 Apple 的向应用添加包依赖说明,使用 Apple 运行时的 GitHub URL:https://github.com/rive-app/rive-ios。

或者,你可以手动添加依赖,将以下内容添加到你的 Package.swift 文件中:

dependencies: [
.package(url: "https://github.com/rive-app/rive-ios", from: "6.13.0")
]

然后将依赖添加到你的 target:

targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "RiveRuntime", package: "rive-ios")
]
)
]

导入 Rive

新的 Apple 运行时与旧版运行时在同一个 Swift 包和 CocoaPods pod 中可用。两个运行时 API 都在同一个包中,因此你可以使用相同的导入语句导入运行时。

import RiveRuntime

创建 Worker

Worker 负责处理 Rive 运行时中的并发。此类型负责启动后台线程进行处理,此外还处理全局(带外)资源。

Worker 必须在 Rive 使用期间保持存活。File 会创建对 Worker 的强引用,因此 Worker 至少会在 File 使用期间保持存活,除非在文件之外保留了 Worker 的引用。

// 在 async 上下文中
let worker = try await Worker()

有关线程的更多信息,请参见线程

加载文件

创建 Worker 后,你可以继续创建 File。每个 File 对象需要一个源和一个 worker。

File 初始化器标记为 @MainActor

// 本地文件
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
// 远程 URL
let worker = try await Worker()
let url: URL = URL(string: "https://example.com/my_file.riv")!
let file = try await File(source: .url(url), worker: worker)
// Data 数据
let worker = try await Worker()
let data: Data = ...
let file = try await File(source: .data(data), worker: worker)

添加视图

创建 File 后,你可以继续创建 Rive 对象。此对象定义视图的配置。最基本的实现仅通过一个文件创建;Rive 将自动加载默认画板和状态机进行渲染。此外,如果画板包含默认视图模型实例,它将自动绑定到状态机。

创建 Rive 对象后,你可以初始化视图。在 UIKit 中,通过创建 RiveUIView 并将其添加到视图层次结构中。在 SwiftUI 中,使用 RiveUIViewRepresentableAsyncRiveUIViewRepresentable 类型。

// UIKit
let riveView = RiveUIView({
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
return try await Rive(file: file)
})
view.addSubview(riveView)
// SwiftUI
var body: some View {
// 创建 Rive 对象后,你可以初始化视图。
RiveUIViewRepresentable(rive)
}
// SwiftUI (异步)
var body: some View {
// 创建 Rive 对象后,你可以初始化视图。
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
return try await Rive(file: file)
}
}

上述示例使用文件的默认画板和状态机,如果可用,将默认视图模型实例绑定到状态机。这是在不指定画板或状态机的情况下初始化 Rive 对象时的默认行为。

或者,你也可以指定要使用的画板和状态机。有关手动选择画板的文档和示例,请参见画板文档。有关手动选择状态机的文档和示例,请参见状态机文档。

数据绑定

数据绑定是一项功能,允许你从代码动态更新 Rive 图形,包括字符串、数字、布尔值等。

默认情况下,创建 Rive 对象将自动数据绑定到其画板的默认视图模型实例。但是,数据绑定有三种选项:

// 自动绑定
let file = try await File(...)
// 自动查找(编辑器中的)默认视图模型实例,绑定到 Rive 对象的画板。
// 下面将使用默认画板和状态机,默认视图模型实例将绑定到状态机。
// 如果不传入 dataBind 参数,这是默认值。
let rive = try await Rive(file: file, dataBind: .auto)

// 然后你可以通过 viewModelInstance 属性访问绑定的视图模型实例。
let viewModelInstance = rive.viewModelInstance
// 绑定实例
let file = try await File(...)
let viewModelInstance = try await file.createViewModelInstance(...)
// 将提供的视图模型实例绑定到 Rive 对象的画板。
// 下面将使用默认画板和状态机,提供的视图模型实例将绑定到状态机。
let rive = try await Rive(file: file, dataBind: .instance(viewModelInstance))
// 不绑定
let file = try await File(...)
let artboard = try await file.createArtboard()
let stateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)
// 如果你已手动将视图模型实例绑定到状态机,可以选择退出数据绑定。
stateMachine.bindViewModelInstance(viewModelInstance)
let rive = try await Rive(file: file, dataBind: .none)

设置数据绑定后,你可以在运行时更新和监听数据绑定属性。

// 设置值
let rive = try await Rive(...)
let viewModelInstance = rive.viewModelInstance
let stringProperty = StringProperty(path: "path/to/string")
viewModelInstance.setValue(of: stringProperty, to: "Hello, Rive")
// 获取值
let rive = try await Rive(...)
let viewModelInstance = rive.viewModelInstance
let stringProperty = StringProperty(path: "path/to/string")

// 获取属性的当前值
let value = try await viewModelInstance.value(of: stringProperty)

// 获取属性的值流,当属性发生变化时发出新值。
let valueStream = viewModelInstance.valueStream(of: stringProperty)
do {
for try await value in valueStream {
print(value)
}
} catch let error as ViewModelInstanceError {
print(error)
} catch {
print(error)
}

示例

有关基本用法,请参见示例应用中的 Marty 示例。

有关更完整的示例,请参见示例应用中的 Quick Start 示例,它演示了如何使用数据绑定。

有关如何暂停和恢复动画以及设置帧率的示例,请参见示例应用中的 Player 示例。

旧版运行时

安装运行时

Swift Package Manager

要通过 Swift Package Manager 安装,在 Xcode 的包查找器中搜索 rive-ios 或完整的 GitHub 路径:https://github.com/rive-app/rive-ios

CocoaPods

将以下内容添加到你的 Podspec 文件:

pod 'RiveRuntime'

导入 Rive

在使用 Rive 运行时的文件顶部添加以下内容:

import RiveRuntime

使用方式

你将使用的主要对象是 RiveViewModel。它负责创建 Rive 资源并与之交互。

SwiftUI
// 本地文件
struct AnimationView: View {
var body: some View {
RiveViewModel(fileName: "cool_rive_animation").view()
}
}
// 远程 URL
struct AnimationView: View {
var body: some View {
RiveViewModel(
webURL: "https://cdn.rive.app/animations/off_road_car_v7.riv"
).view()
}
}
UIKit

你可以通过代码将 Rive 添加到视图控制器,方法是创建 RiveViewModel,让它创建一个新的 RiveView,然后将其添加到视图层次结构中。

或者,你也可以使用 Storyboard 将 Rive 添加到控制器,方法是创建 RiveViewModel,并将其视图设置为在 Storyboard 中创建的 RiveView

// 编程方式
class AnimationViewController: UIViewController {
var simpleVM = RiveViewModel(fileName: "cool_rive_animation")

override func viewWillAppear(_ animated: Bool) {
let riveView = simpleVM.createRiveView()
view.addSubview(riveView)
riveView.frame = view.bounds
}
}
// Storyboard
class AnimationViewController: UIViewController {
@IBOutlet weak var riveView: RiveView!
var simpleVM = RiveViewModel(fileName: "cool_rive_animation")

override public func viewDidLoad() {
simpleVM.setView(riveView)
}
}

播放 / 暂停

当前运行时

// UIKit
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
riveView.isPaused = true // 或 false 恢复
// SwiftUI
@State var isPaused = false
var body: some View {
RiveUIViewRepresentable(rive)
.paused(isPaused)
}
// SwiftUI (异步)
@State var isPaused = false
var body: some View {
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
return rive
}
.paused(isPaused)
}

旧版运行时

let viewModel = RiveViewModel(fileName: "...")
viewModel.pause() // 或 play() 恢复

帧率

当前运行时

// UIKit
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
// 设置固定帧率
riveView.frameRate = .fps(30)
// 设置帧率范围(注意:在 iOS < 15 / macOS < 14 上,这与使用 .fps 相同)
riveView.frameRate = .range(minimum: 30, maximum: 60)
// 恢复默认帧率
riveView.frameRate = .default
// SwiftUI
var body: some View {
RiveUIViewRepresentable(rive)
.frameRate(.fps(30))
// 或
.frameRate(.range(minimum: 30, maximum: 60))
// 或
.frameRate(.default)
}
// SwiftUI (异步)
var body: some View {
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
return rive
}
.frameRate(.fps(30))
// 或
.frameRate(.range(minimum: 30, maximum: 60))
// 或
.frameRate(.default)
}

旧版运行时

let viewModel = RiveViewModel(fileName: "...")
viewModel.setPreferredFramesPerSecond(preferredFramesPerSecond: 30)
// 或者,在 iOS >= 15 / macOS >= 14 上
viewModel.setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange(minimum: 30, maximum: 60))

线程

当前运行时

新的运行时通过引入 Worker 对象支持多线程。

// 在 async 上下文中
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)

Worker 对象负责为 Rive 实例创建和管理后台线程。

每个 worker 共享带外资源,如图像、字体和音频。这意味着使用同一个 worker 初始化的每个 File 将共享相同的带外资源。

一个 worker 大致相当于一个后台线程。如果你渲染多个重量级 Rive 图形,可以为每个文件创建一个 Worker,让每个文件在各自的后台线程上处理。

Worker 对象的数量受系统可用性限制;具体来说,WorkerDispatchQueue 支持,它处理线程的创建和重用。

需要注意的是,虽然支持多线程,但 Rive 对象的 API 调用仍必须在主 actor 上进行。这通过将函数和类型标记为 @MainActor 在编译时强制执行。

共享 Worker

对于需要在多个 File 对象之间共享资源(即带外/引用资源)的情况,你可以使用共享的 Worker 对象。以下是一个示例实现。

actor WorkerProvider {
static let shared = WorkerProvider()

@MainActor
private var cachedWorker: Worker?

@MainActor
func worker() async throws -> Worker {
if let cachedWorker {
return cachedWorker
}

let worker = try await Worker()
cachedWorker = worker
return worker
}
}

然后,在创建 Rive 对象时,你可以使用共享的 worker 提供者来获取 worker。

let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)

旧版运行时

旧版运行时目前在主线程上是单线程的。这意味着所有 Rive 调用必须在主线程上进行。建议如果你在后台线程上,应在进行任何 Rive 调用之前切换到主队列。

日志

当前运行时

新的运行时尚未包含日志功能,但将在不久的将来添加。

旧版运行时

启用日志只需将 RiveLogger.isEnabled 设置为 true

RiveLogger.isEnabled = true

有关日志级别、类别和详细日志的更多详细信息,请参见日志页面。

请参阅后续运行时页面,了解如何控制动画播放、状态机等。

示例应用

你可以从 Rive GitHub 仓库运行我们的 Apple 示例应用。

git clone https://github.com/rive-app/rive-ios

在 Xcode 中打开 Example-iOS 应用,并确保选择 Preview (iOS)Preview (macOS) scheme。其他 scheme 用于开发目的,需要额外配置,参见 CONTRIBUTING.MD

Image

资源

GitHub:https://github.com/rive-app/rive-ios

示例: