数据绑定
数据绑定允许你将 Rive 文件中在编辑器里定义的属性连接到运行时代码。通过视图模型(View Model)和视图模型实例(View Model Instance),你可以读取、写入和监听字符串、数字、布尔值、颜色、枚举、触发器、图片、列表和 Artboard 等绑定属性。
数据绑定的基本流程通常是:
- 在 Rive 编辑器中定义视图模型和属性。
- 在运行时从
File中获取或引用视图模型。 - 创建一个视图模型实例。
- 将该实例绑定到 Artboard 或状态机。
- 在代码中读取、写入或监听实例上的属性。
视图模型
视图模型定义了一组可绑定属性。它类似于一个数据结构或接口,用于描述运行时可以访问哪些数据。视图模型本身通常不是你直接操作的运行时数据对象;真正保存值的是视图模型实例。
当前 Apple Runtime
视图模型不是单独的类型;更准确地说,它是在从 File 创建视图模型实例时使用的来源。
你可以通过 ViewModelSource 类型定义视图模型的来源。
case artboardDefault(Artboard) // References the default view model for an Artboard
case name(String) // References a view model from a file by name
这些来源会与获取视图模型实例的 API 一起使用。更多信息请参见视图模型实例。
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
let file = riveViewModel.riveModel!.riveFile
// Data binding view model by name
let viewModelByName = file.viewModelNamed("...")
// Data binding view model by index
for index in 0..<file.viewModelCount {
let viewModelByIndex = file.viewModel(at: index)
}
// Default data binding view model for an artboard
let artboard = riveViewModel.riveModel!.artboard
let viewModelForArtboard = file.viewModel(for: artboard)
视图模型实例
视图模型实例是视图模型在运行时的具体数据对象。你会从实例中读取属性值、写入属性值、触发 trigger,或者监听属性变化。
一个视图模型可以有多个实例,例如空实例、默认实例,或在 Rive 文件中命名的实例。
当前 Apple Runtime
以下内容假设你已经阅读过 Apple 概览。
// From a file
let file: File = ...
// When using a view model by name:
// A blank view model instance
var blankInstance = try await file.createViewModelInstance(.blank(from: .name("ViewModel")))
// The default instance for the view model
var defaultInstance = try await file.createViewModelInstance(.viewModelDefault(from: .name("ViewModel")))
// An instance by name from the view model
var namedInstance = try await file.createViewModelInstance(.name("Instance", from: .name("ViewModel")))
// Alternatively, using the default view model for an artboard
let artboard: Artboard = ...
// A blank view model instance
blankInstance = try await file.createViewModelInstance(.blank(from: .artboardDefault(artboard)))
// The default instance for the view model
defaultInstance = try await file.createViewModelInstance(.viewModelDefault(from: .artboardDefault(artboard)))
// An instance by name from the view model
namedInstance = try await file.createViewModelInstance(.name("Instance", from: .artboardDefault(artboard)))
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
let viewModel = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!
// Create blank
let blankInstance = viewModel.createInstance()
// Create default
let defaultInstance = viewModel.createDefaultInstance()
// Create by index
for index in 0..<viewModel.instanceCount {
let instanceByIndex = viewModel.createInstance(fromIndex: index)
}
// Create by name
for name in viewModel.instanceNames {
let instanceByName = viewModel.createInstance(fromName: name)
}
绑定
创建视图模型实例后,需要将其绑定到运行中的状态机或 Artboard。绑定之后,编辑器中连接到该视图模型的元素就会使用实例中的数据。
当前 Apple Runtime
给定以下示例代码:
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 类型自动进行数据绑定:
// Automatically find a default view model instance to bind. This is the default value, if you do not pass in a dataBind argument.
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto)
// Bind a view model instance
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .viewModelInstance(viewModelInstance))
// Do not bind. This assumes you have manually bound a view model instance earlier
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .none)
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
let artboard = riveViewModel.riveModel!.artboard,
let instance = riveViewModel.riveModel!.riveFile.defaultViewModel(for: artboard).createDefaultInstance()!
// Apply the instance to the state machine (preferred)
// Applying to a state machine will automatically bind to its artboard
riveViewModel.riveModel!.stateMachine.bind(instance)
// Alternatively, apply the instance to the artboard
artboard.bind(viewModelInstance: instance)
自动绑定
自动绑定会尝试为当前 Artboard 或状态机找到合适的默认视图模型实例,并自动将其绑定。对于简单场景,这可以减少手动创建和绑定实例的代码。
当前 Apple Runtime
给定以下示例代码:
let file: File = ...
let artboard: Artboard = try await file.createArtboard()
let stateMachine: StateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)
创建 Rive 对象时,你可以选择自动绑定:
// Automatically find a default view model instance to bind. This is the default value, if you do not pass in a dataBind argument.
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto)
// or
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine)
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to `instance` to later access properties
// The instance may change as state machines and artboards change
}
// If you'd like to disable autoBind after enabling…
riveViewModel.riveModel!.disableAutoBind()
属性
属性是视图模型中可绑定的数据字段。Rive 支持多种属性类型,包括字符串、数字、布尔值、颜色、枚举、触发器、图片、列表和 Artboard。运行时通过视图模型实例访问这些属性。
列出属性
你可以查询视图模型中定义的属性,以了解属性名称、类型和可用元数据。
当前 Apple Runtime
let file: File = ...
let properties = try await file.getProperties(of: "ViewModel")
for property in properties {
print(property.type) // enum of string, number, boolean, etc
print(property.name) // The name of the property within the view model
print(property.metaData) // Additional metadata for the property, if available
}
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
let viewModel = riveViewModel.riveModel!.file.viewModelNamed(...)!
for property in viewModel.properties {
print(property.type) // String, number, boolean, etc
print(property.name) // The name of the property within the view model
}
读取和写入属性
读取和写入属性是数据绑定最常见的操作。你可以通过属性路径访问视图模型实例上的属性,并根据属性类型读取或更新其值。
当前 Apple Runtime
属性类型只是对属性路径和返回类型的轻量封装。
所有属性 API(例如 setter、getter 和 trigger)都作为 ViewModelInstance 对象的一部分提供。
String
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")
Number
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: numberValue, to: 9001)
Bool
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)
Color
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))
Enum
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")
Trigger
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let triggerProperty = TriggerProperty(path: "path/to/trigger")
viewModelInstance.fire(trigger: triggerProperty)
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
// Strings
let stringProperty = instance.stringProperty(fromPath: "...")!
// Updating its value
stringProperty.value = "Hello, Rive"
// Get its value
print(stringProperty.value)
// You can also set and get values without storing a strong reference
instance.stringProperty(fromPath: "...").value = "Hello again, Rive"
// Numbers
let numberProperty = instance.numberProperty(fromPath: "...")!
// Updating its value
numberProperty.value = 1337
// Get its value
print(numberProperty.value)
// You can also set and get values without storing a strong reference
instance.numberProperty(fromPath: "...").value = 1337
// Booleans
let booleanProperty = instance.booleanProperty(fromPath: "...")!
// Updating its value
booleanProperty.value = true
// Get its value
print(booleanProperty.value)
// You can also set and get values without storing a strong reference
instance.booleanProperty(fromPath: "...").value = true
// Colors
let colorProperty = instance.colorProperty(fromPath: "...")!
// Updating its value, which is a UIColor/NSColor, so all static helpers apply.
colorProperty.value = .red
// Get its value
print(colorProperty.value)
// You can also set and get values without storing a strong reference
instance.colorProperty(fromPath: "...").value = .red
// Enums
let enumProperty = instance.enumProperty(fromPath: "...")!
// Updating its value
enumProperty.value = "Foo"
// Get its value
print(enumProperty.value)
// Print all possible values
print(enumProperty.values)
// You can also set and get values without storing a strong reference
instance.enumProperty(fromPath: "...").value = "Foo"
// Trigger
let triggerProperty = instance.triggerProperty(fromPath: "...")!
// Fire the trigger
triggerProperty.trigger()
嵌套属性路径
视图模型可以包含嵌套结构。你可以通过完整路径访问嵌套属性。
当前 Apple Runtime
属性类型不再是引用类型,并且在初始化属性值类型时需要提供属性名称或完整路径。不再提供用于链式访问嵌套属性的 API。
使用方式请参见属性。
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
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")
可观察性
你可以监听属性值的变化,并在值变化或 trigger 被触发时响应。这适合用于将 Rive 动画状态同步回应用逻辑。
当前 Apple Runtime
属性监听使用 Swift Concurrency 的 async throwing stream 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 {
// The thrown error should always be a ViewModelInstanceError type
print(error)
} catch {
print(error)
}
对于 trigger,你可以在 ViewModelInstance 对象上调用 stream(of:) 方法来监听。它会返回一个 Void 值的 stream,你可以忽略这些值。
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 {
// The thrown error should always be a ViewModelInstanceError type
print(error)
} catch {
print(error)
}
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
// Get the string property
let stringProperty = instance.stringProperty(fromPath: "...")!
// Add a listener
let listener = stringProperty.addListener { newValue in
print(newValue)
}
// Remove a listener, where listener is the return value of addListener
stringProperty.removeListener(listener)
// Trigger properties can also be listened to for when they are triggered
instance.triggerProperty(fromPath: "...")!.addListener {
print("Triggered!")
}
图片
图片属性允许你在运行时替换 Rive 文件中绑定的图片资源。
当前 Apple Runtime
要设置图片,首先需要通过 Worker 解码图片。这个 Worker 必须与初始化 File 时使用的 Worker 相同;你要设置的图片属性也来自该 File 创建的视图模型实例。
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)
Legacy Apple Runtime
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
// Create a RiveRenderImage from data
let data = Data(...)
var image = RiveRenderImage(data: data)! // This can return nil if the data is not a valid image
// Or, create a RiveRenderImage from a UIImage
image = RiveRenderImage(image: UIImage(named: "my_image")!, format: .png)! // This can return nil if the image is not a valid jpg or png image
let imageProperty = viewModelInstance.imageProperty(fromPath: "image")!
// Once you have your data binding view model instance, you can set the image property value
imageProperty.setValue(image)
// You can also pass nil to clear the image
imageProperty.setValue(nil)
列表
列表属性可以包含多个视图模型实例。你可以追加、插入、交换、删除和读取列表中的实例。
当前 Apple Runtime
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let listProperty = ListProperty(path: "path/to/list")
let size = try await viewModelInstance.size(of: listProperty)
// Result: [newInstance]
let newInstance = try await file.createViewModelInstance(...)
viewModelInstance.appendInstance(newInstance, to: listProperty)
// Result: [insertedInstance, newInstance]
let insertedInstance = try await file.createViewModelInstance(...)
viewModelInstance.insertInstance(insertedInstance, to: listProperty, at: 0)
// Result: [newInstance, insertedInstance]
viewModelInstance.swapInstance(atIndex: 0, withIndex: 1, in: listProperty)
// Result: [newInstance]
viewModelInstance.removeInstance(at: 1, from: listProperty)
// Result: newInstance
let _ = viewModelInstance.value(of: listProperty, at: 0)
// Result: []
viewModelInstance.removeInstance(newInstance, from: listProperty)
// Result: 0
let finalSize = try await viewModelInstance.size(of: listProperty)
Legacy Apple Runtime
let listProperty = viewModelInstance.listProperty(fromPath: "list")!
// Create a new view model instance and add it to the end of the list
let firstInstance = viewModel.createInstanceByName("First Instance")!
listProperty.add(firstInstance)
// Create a new view model instance and add it to the beginning of the list
let secondInstance = myViewModel.createInstanceByName("Second Instance")!
listProperty.add(secondInstance, atIndex: 0)
// Swap the first and second instances
listProperty.swapInstance(atIndex: 0, withInstanceAtIndex: 1)
// Remove both instances
listProperty.removeInstance(secondInstance)
listProperty.removeInstance(atIndex: 0)
// Get and print the size of the list
print(listProperty.size) // Prints 0
Artboard
Artboard 属性允许你在运行时替换绑定到某个属性的 Artboard。
当前 Apple Runtime
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)
Legacy Apple Runtime
使用 RiveDataBindingViewModel.Instance 对象上的 artboardProperty 方法获取 artboard 属性。
然后使用 artboard 属性对象上的 setValue 方法设置新的 artboard 值。
setValue 接收一个 RiveBindableArtboard 对象,它是 Artboard 的包装对象,可用于设置 artboard 属性值。
你可以通过 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)
枚举
枚举属性用于在一组预定义字符串值之间切换。你可以读取当前枚举值、设置新的枚举值,并在 Legacy API 中读取可用枚举值列表。设置枚举值时,需要确保传入的字符串与 Rive 文件中定义的枚举选项匹配。
示例
请参阅示例应用中的 Data Binding view 获取演示。