跳到主要内容

数据绑定

概览

数据绑定是一项功能,允许你动态更新 Rive 图形中的元素,包括文本、图像、颜色等。它通过视图模型和视图模型实例将代码连接到编辑器中的数据绑定元素。

视图模型

视图模型定义了可以绑定到 Rive 图形的属性结构。

当前运行时

视图模型本身不是独立的类型;而是在从 File 创建视图模型实例时的源。

你可以通过 ViewModelSource 类型定义视图模型的来源。

case artboardDefault(Artboard) // 引用画板的默认视图模型
case name(String) // 按名称引用文件中的视图模型

这些源与获取视图模型实例结合使用。有关更多信息,请参见视图模型实例

旧版运行时

let riveViewModel = RiveViewModel(...)
let file = riveViewModel.riveModel!.riveFile

// 按名称获取数据绑定视图模型
let viewModelByName = file.viewModelNamed("...")

// 按索引获取数据绑定视图模型
for index in 0..<file.viewModelCount {
let viewModelByIndex = file.viewModel(at: index)
}

// 获取画板的默认数据绑定视图模型
let artboard = riveViewModel.riveModel!.artboard
let viewModelForArtboard = file.viewModel(for: artboard)

视图模型实例

当前运行时

以下部分假设你已阅读了 Apple 概述。

// 从文件
let file: File = ...

// 使用按名称的视图模型:
// 空视图模型实例
var blankInstance = try await file.createViewModelInstance(.blank(from: .name("ViewModel")))
// 视图模型的默认实例
var defaultInstance = try await file.createViewModelInstance(.viewModelDefault(from: .name("ViewModel")))
// 视图模型中按名称获取的实例
var namedInstance = try await file.createViewModelInstance(.name("Instance", from: .name("ViewModel")))

// 或者,使用画板的默认视图模型
let artboard: Artboard = ...
// 空视图模型实例
blankInstance = try await file.createViewModelInstance(.blank(from: .artboardDefault(artboard)))
// 视图模型的默认实例
defaultInstance = try await file.createViewModelInstance(.viewModelDefault(from: .artboardDefault(artboard)))
// 视图模型中按名称获取的实例
namedInstance = try await file.createViewModelInstance(.name("Instance", from: .artboardDefault(artboard)))

旧版运行时

let riveViewModel = RiveViewModel(...)
let viewModel = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!

// 创建空白实例
let blankInstance = viewModel.createInstance()

// 创建默认实例
let defaultInstance = viewModel.createDefaultInstance()

// 按索引创建
for index in 0..<viewModel.instanceCount {
let instanceByIndex = viewModel.createInstance(fromIndex: index)
}

// 按名称创建
for name in viewModel.instanceNames {
let instanceByName = viewModel.createInstance(fromName: name)
}

绑定

当前运行时

给定以下示例代码:

let file: File = ...
let artboard: Artboard = try await file.createArtboard()
let stateMachine: StateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)

你可以手动将视图模型实例绑定到状态机:

stateMachine.bindViewModelInstance(viewModelInstance)

或者,你可以利用 Rive 类型(自动)数据绑定视图模型实例:

// 自动查找默认视图模型实例进行绑定。如果不传入 dataBind 参数,这是默认值。
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto)
// 绑定视图模型实例
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .instance(viewModelInstance))
// 不绑定。这假设你之前已手动绑定视图模型实例
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .none)

旧版运行时

let riveViewModel = RiveViewModel(...)
let artboard = riveViewModel.riveModel!.artboard,
let instance = riveViewModel.riveModel!.riveFile.defaultViewModel(for: artboard).createDefaultInstance()!

// 将实例应用到状态机(推荐)
// 应用到状态机会自动绑定到其画板
riveViewModel.riveModel!.stateMachine.bind(instance)

// 或者,将实例应用到画板
artboard.bind(viewModelInstance: instance)

自动绑定

当前运行时

给定以下示例代码:

let file: File = ...
let artboard: Artboard = try await file.createArtboard()
let stateMachine: StateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)

创建 Rive 对象时,你可以选择自动绑定:

// 自动查找默认视图模型实例进行绑定。如果不传入 dataBind 参数,这是默认值。
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto)
// 或
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine)

旧版运行时

let riveViewModel = RiveViewModel(...)
riveViewModel.riveModel?.enableAutoBind { instance in
// 存储对 instance 的引用以便稍后访问属性
// 实例可能随着状态机和画板的变化而变化
}

// 如果你想在启用后禁用 autoBind...
riveViewModel.riveModel!.disableAutoBind()

属性

列出属性

当前运行时

let file: File = ...
let properties = try await file.getProperties(of: "ViewModel")
for property in properties {
print(property.type) // 字符串、数字、布尔值等枚举
print(property.name) // 视图模型中属性的名称
print(property.metaData) // 属性的附加元数据(如果可用)
}

旧版运行时

let riveViewModel = RiveViewModel(...)
let viewModel = riveViewModel.riveModel!.file.viewModelNamed(...)!
for property in viewModel.properties {
print(property.type) // String、number、boolean 等
print(property.name) // 视图模型中属性的名称
}

读写属性

当前运行时

属性类型是对属性路径和返回类型的轻量包装。

所有属性 API(例如设置器、获取器和触发器)都作为 ViewModelInstance 对象的一部分提供。

// 字符串
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)

// String
let stringProperty = StringProperty(path: "path/to/string")
let stringValue = try await viewModelInstance.value(of: stringProperty)
viewModelInstance.setValue(of: stringProperty, to: "value")
// 数字
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)

let numberProperty = NumberProperty(path: "path/to/number")
let numberValue = try await viewModelInstance.value(of: numberProperty)
viewModelInstance.setValue(of: numberProperty, to: 9001)
// 布尔值
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)

let boolProperty = BoolProperty(path: "path/to/bool")
let boolValue = try await viewModelInstance.value(of: boolProperty)
viewModelInstance.setValue(of: boolProperty, to: true)
// 颜色
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)

let colorProperty = ColorProperty(path: "path/to/color")
let colorValue = try await viewModelInstance.value(of: colorProperty)
viewModelInstance.setValue(of: colorProperty, to: Color(red: 255, green: 255, blue: 255, alpha: 255))
// 枚举
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)

let enumProperty = EnumProperty(path: "path/to/enum")
let enumValue = try await viewModelInstance.value(of: enumProperty)
viewModelInstance.setValue(of: enumProperty, to: "value")
// 触发器
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)

let triggerProperty = TriggerProperty(path: "path/to/trigger")
viewModelInstance.fire(trigger: triggerProperty)

旧版运行时

let riveViewModel = RiveViewModel(...)

var viewModelInstance: RiveDataBindingViewModel.Instance!

// 你可以在启用自动绑定时获取视图模型实例
riveViewModel.riveModel?.enableAutoBind { instance in
// 存储对 instance 的引用
viewModelInstance = instance
}

// 或者,你可以手动创建视图模型实例
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!

// 字符串
let stringProperty = instance.stringProperty(fromPath: "...")!
// 更新其值
stringProperty.value = "Hello, Rive"
// 获取其值
print(stringProperty.value)

// 你也可以在不存储强引用的情况下设置和获取值
instance.stringProperty(fromPath: "...").value = "Hello again, Rive"

// 数字
let numberProperty = instance.numberProperty(fromPath: "...")!
// 更新其值
numberProperty.value = 1337
// 获取其值
print(numberProperty.value)

// 你也可以在不存储强引用的情况下设置和获取值
instance.numberProperty(fromPath: "...").value = 1337

// 布尔值
let booleanProperty = instance.booleanProperty(fromPath: "...")!
// 更新其值
booleanProperty.value = true
// 获取其值
print(booleanProperty.value)

// 你也可以在不存储强引用的情况下设置和获取值
instance.booleanProperty(fromPath: "...").value = true

// 颜色
let colorProperty = instance.colorProperty(fromPath: "...")!
// 更新其值,它是 UIColor/NSColor,因此所有静态辅助方法都适用。
colorProperty.value = .red
// 获取其值
print(colorProperty.value)

// 你也可以在不存储强引用的情况下设置和获取值
instance.colorProperty(fromPath: "...").value = .red

// 枚举
let enumProperty = instance.enumProperty(fromPath: "...")!
// 更新其值
enumProperty.value = "Foo"
// 获取其值
print(enumProperty.value)
// 打印所有可能的值
print(enumProperty.values)

// 你也可以在不存储强引用的情况下设置和获取值
instance.enumProperty(fromPath: "...").value = "Foo"

// 触发器
let triggerProperty = instance.triggerProperty(fromPath: "...")!
// 触发触发器
triggerProperty.trigger()

嵌套属性路径

当前运行时

属性类型不再是引用类型,在初始化属性值类型时需要属性的名称或完整路径。不再有链接嵌套属性的 API。

有关用法详细信息,请参见属性

旧版运行时

let riveViewModel = RiveViewModel(...)

var viewModelInstance: RiveDataBindingViewModel.Instance!

// 你可以在启用自动绑定时获取视图模型实例
riveViewModel.riveModel?.enableAutoBind { instance in
// 存储对 instance 的引用
viewModelInstance = instance
}

// 或者,你可以手动创建视图模型实例
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!

let nestedNumberByChain = instance
.viewModelInstanceProperty(fromPath: "Nested View Model")
.viewModelInstanceProperty(fromPath: "Another Nested View Model")
.numberProperty(fromPath: "Number")

let nestedNumberByPath = instance.numberProperty(fromPath: "Nested View Model/Another Nested View Model/Number")

可观察性

当前运行时

属性监听器利用 Swift Concurrency 的异步抛出流 API。如果属性返回值,你可以通过在 ViewModelInstance 对象上调用 valueStream(of:) 方法来监听其变化。

let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let stringProperty = StringProperty(path: "path/to/string")
let valueStream = viewModelInstance.valueStream(of: stringProperty)
do {
for try await value in valueStream {
print(value)
}
} catch let error as ViewModelInstanceError {
// 抛出的错误应始终是 ViewModelInstanceError 类型
print(error)
} catch {
print(error)
}

对于触发器,你可以通过在 ViewModelInstance 对象上调用 stream(of:) 方法来监听它们。这会返回一个 Void 值流,可以忽略。

let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let triggerProperty = TriggerProperty(path: "path/to/trigger")
let triggerStream = viewModelInstance.stream(of: triggerProperty)
do {
for try await _ in triggerStream {
print("Trigger fired!")
}
} catch let error as ViewModelInstanceError {
// 抛出的错误应始终是 ViewModelInstanceError 类型
print(error)
} catch {
print(error)
}

旧版运行时

let riveViewModel = RiveViewModel(...)

var viewModelInstance: RiveDataBindingViewModel.Instance!

// 你可以在启用自动绑定时获取视图模型实例
riveViewModel.riveModel?.enableAutoBind { instance in
// 存储对 instance 的引用
viewModelInstance = instance
}

// 或者,你可以手动创建视图模型实例
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!

// 获取字符串属性
let stringProperty = instance.stringProperty(fromPath: "...")!

// 添加监听器
let listener = stringProperty.addListener { newValue in
print(newValue)
}

// 移除监听器,其中 listener 是 addListener 的返回值
stringProperty.removeListener(listener)

// 触发器属性也可以监听其触发事件
instance.triggerProperty(fromPath: "...")!.addListener {
print("Triggered!")
}

图像

当前运行时

要设置图像,你首先需要从 Worker 解码图像。这必须是初始化 File 时使用的同一个 Worker,你从该文件设置视图模型实例的图像属性。

let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let viewModelInstance = file.createViewModelInstance(...)
let imageProperty = ImageProperty(path: "path/to/image")
let imageData: Data = ...
let image = try await decodeImage(from: imageData)
viewModelInstance.setValue(of: imageProperty, to: image)

旧版运行时

let riveViewModel = RiveViewModel(...)

var viewModelInstance: RiveDataBindingViewModel.Instance!

// 你可以在启用自动绑定时获取视图模型实例
riveViewModel.riveModel?.enableAutoBind { instance in
// 存储对 instance 的引用
viewModelInstance = instance
}

// 或者,你可以手动创建视图模型实例
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!

// 从 Data 创建 RiveRenderImage
let data = Data(...)
var image = RiveRenderImage(data: data)! // 如果数据不是有效图像,可能返回 nil

// 或者,从 UIImage 创建 RiveRenderImage
image = RiveRenderImage(image: UIImage(named: "my_image")!, format: .png)! // 如果图像不是有效的 jpg 或 png 图像,可能返回 nil

let imageProperty = viewModelInstance.imageProperty(fromPath: "image")!

// 获取数据绑定视图模型实例后,你可以设置图像属性值
imageProperty.setValue(image)

// 你也可以传递 nil 来清除图像
imageProperty.setValue(nil)

列表

当前运行时

let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let listProperty = ListProperty(path: "path/to/list")

let size = try await viewModelInstance.size(of: listProperty)

// 结果:[newInstance]
let newInstance = try await file.createViewModelInstance(...)
viewModelInstance.appendInstance(newInstance, to: listProperty)

// 结果:[insertedInstance, newInstance]
let insertedInstance = try await file.createViewModelInstance(...)
viewModelInstance.insertInstance(insertedInstance, to: listProperty, at: 0)

// 结果:[newInstance, insertedInstance]
viewModelInstance.swapInstance(atIndex: 0, withIndex: 1, in: listProperty)

// 结果:[newInstance]
viewModelInstance.removeInstance(at: 1, from: listProperty)

// 结果:newInstance
let _ = viewModelInstance.value(of: listProperty, at: 0)

// 结果:[]
viewModelInstance.removeInstance(newInstance, from: listProperty)

// 结果:0
let size = try await viewModelInstance.size(of: listProperty)

旧版运行时

let listProperty = viewModelInstance.listProperty(fromPath: "list")!

// 创建新的视图模型实例并将其添加到列表末尾
let firstInstance = viewModel.createInstanceByName("First Instance")!
listProperty.add(firstInstance)

// 创建新的视图模型实例并将其添加到列表开头
let secondInstance = myViewModel.createInstanceByName("Second Instance")!
listProperty.add(secondInstance, atIndex: 0)

// 交换第一个和第二个实例
listProperty.swapInstance(atIndex: 0, withInstanceAtIndex: 1)

// 移除两个实例
listProperty.removeInstance(secondInstance)
listProperty.removeInstance(atIndex: 0)

// 获取并打印列表大小
print(listProperty.size) // 打印 0

画板

当前运行时

let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let artboardProperty = ArtboardProperty(path: "path/to/artboard")
let artboard = try await file.createArtboard(...)
viewModelInstance.setValue(of: artboardProperty, to: artboard)

旧版运行时

使用 RiveDataBindingViewModel.Instance 对象上的 artboardProperty 方法获取画板属性。

然后使用画板属性对象上的 setValue 方法设置新的画板值。

setValue 接受一个 RiveBindableArtboard 对象,它是可用于设置画板属性值的画板包装器。

你可以使用 RiveFile 对象上的 bindableArtboard 方法获取 RiveBindableArtboard 对象。

let artboardProperty = instance.artboardProperty(fromPath: "Artboard")!

let components = RiveFile(...)
let bindableArtboard = components.bindableArtboard(at: 0)!
let bindableArtboard2 = components.bindableArtboard(withName: "...")!

artboardProperty.setValue(bindableArtboard)

枚举

示例

有关演示,请参见示例应用中的 Data Binding view