Skip to content

Commit fbf2ed1

Browse files
committed
feat: add data binding config
1 parent dd35eef commit fbf2ed1

5 files changed

Lines changed: 104 additions & 9 deletions

File tree

example/app/(examples)/DataBinding.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import {
77
View,
88
} from 'react-native';
99
import Rive, {
10+
AutoBind,
11+
BindByIndex,
12+
BindByName,
13+
BindEmpty,
1014
Fit,
1115
useRive,
1216
useRiveColor,
@@ -51,6 +55,10 @@ export default function DataBinding() {
5155
style={styles.animation}
5256
layoutScaleFactor={1}
5357
autoplay={true}
58+
dataBinding={AutoBind(true)}
59+
// dataBinding={BindByIndex(0)}
60+
// dataBinding={BindByName('SomeName')}
61+
// dataBinding={BindEmpty()}
5462
stateMachineName={'State Machine 1'}
5563
resourceName={'rewards'}
5664
/>

ios/RiveReactNativeView.swift

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
88
return self.bridge?.module(for: RiveReactNativeEventModule.self) as? RiveReactNativeEventModule
99
}
1010
private var propertyListeners: [(key: String, property: RiveDataBindingViewModel.Instance.Property, listener: UUID)] = []
11+
private var dataBindingConfig: DataBindingConfig?
1112

1213
// MARK: RiveReactNativeView Properties
1314
private var resourceFromBundle = true
@@ -75,6 +76,32 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
7576
}
7677
}
7778

79+
@objc var dataBinding: [String: Any]? {
80+
didSet {
81+
guard let type = dataBinding?["type"] as? String else { return }
82+
dataBindingConfig = {
83+
switch type {
84+
case "autobind":
85+
if let val = dataBinding?["value"] as? Bool {
86+
return .autoBind(val)
87+
}
88+
case "index":
89+
if let val = dataBinding?["value"] as? NSNumber {
90+
return .index(val.intValue)
91+
}
92+
case "name":
93+
if let val = dataBinding?["value"] as? String {
94+
return .name(val)
95+
}
96+
case "empty":
97+
return .empty
98+
default:
99+
break
100+
}
101+
return nil
102+
}()
103+
}
104+
}
78105

79106
@objc var animationName: String?
80107

@@ -154,6 +181,10 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
154181
if (changedProps.contains("layoutScaleFactor")) {
155182
viewModel?.layoutScaleFactor = layoutScaleFactor.doubleValue
156183
}
184+
185+
if (changedProps.contains("dataBinding")) {
186+
viewModel.map { configureDataBinding(viewModel: $0) }
187+
}
157188
}
158189

159190
private func convertFit(_ fit: String? = nil) -> RiveFit {
@@ -180,6 +211,36 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
180211
return nil;
181212
}
182213

214+
private func configureDataBinding(viewModel: RiveViewModel) {
215+
guard let artboard = viewModel.riveModel?.artboard,
216+
let dataBindingViewModel = viewModel.riveModel?.riveFile.defaultViewModel(for: artboard) else { return }
217+
218+
func bindInstance(_ instance: RiveDataBindingViewModel.Instance?) {
219+
guard let instance = instance else { return }
220+
viewModel.riveModel?.stateMachine?.bind(viewModelInstance: instance)
221+
self.dataBindingViewModelInstance = instance
222+
}
223+
224+
switch dataBindingConfig {
225+
case .autoBind(let autoBind):
226+
if autoBind {
227+
viewModel.riveModel?.enableAutoBind { [weak self] instance in
228+
self?.dataBindingViewModelInstance = instance
229+
}
230+
} else {
231+
viewModel.riveModel?.disableAutoBind()
232+
}
233+
case .index(let index):
234+
bindInstance(dataBindingViewModel.createInstance(fromIndex: UInt(index)))
235+
case .name(let name):
236+
bindInstance(dataBindingViewModel.createInstance(fromName: name))
237+
case .empty:
238+
bindInstance(dataBindingViewModel.createInstance())
239+
case nil:
240+
break
241+
}
242+
}
243+
183244
private func createNewView(updatedViewModel : RiveViewModel){
184245
riveView?.playerDelegate = nil
185246
riveView?.stateMachineDelegate = nil
@@ -206,7 +267,7 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
206267
// Send the "RiveReactNativeLoaded" event
207268
private func sendRiveLoadedEvent() {
208269
guard let loadedTag = generateLoadedTag(),
209-
eventEmitter?.isListenerActive(loadedTag) == true else { return }
270+
eventEmitter?.isListenerActive(loadedTag) == true else { return }
210271
eventEmitter?.sendEvent(withName: loadedTag, body: nil)
211272
}
212273

@@ -227,10 +288,6 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
227288
}
228289

229290
updatedViewModel.layoutScaleFactor = layoutScaleFactor.doubleValue
230-
// In React Native we always autobind to true, until we support a more robust data binding API
231-
updatedViewModel.riveModel?.enableAutoBind({ [weak self] instance in
232-
self?.dataBindingViewModelInstance = instance
233-
})
234291

235292
createNewView(updatedViewModel: updatedViewModel)
236293
requiresLocalResourceReconfigure = false
@@ -252,10 +309,6 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
252309
}
253310

254311
updatedViewModel.layoutScaleFactor = layoutScaleFactor.doubleValue
255-
// In React Native we always autobind to true, until we support a more robust data binding API
256-
updatedViewModel.riveModel?.enableAutoBind({ [weak self] instance in
257-
self?.dataBindingViewModelInstance = instance
258-
})
259312

260313
createNewView(updatedViewModel: updatedViewModel)
261314
}
@@ -787,3 +840,10 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
787840
}
788841
}
789842
}
843+
844+
enum DataBindingConfig {
845+
case autoBind(Bool)
846+
case index(Int)
847+
case name(String)
848+
case empty
849+
}

ios/RiveReactNativeViewManager.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ @interface RCT_EXTERN_MODULE(RiveReactNativeViewManager, RCTViewManager)
1111
RCT_EXPORT_VIEW_PROPERTY(autoplay, BOOL)
1212
RCT_EXPORT_VIEW_PROPERTY(artboardName, NSString)
1313
RCT_EXPORT_VIEW_PROPERTY(referencedAssets, NSDictionary)
14+
RCT_EXPORT_VIEW_PROPERTY(dataBinding, NSDictionary)
1415
RCT_EXPORT_VIEW_PROPERTY(animationName, NSString)
1516
RCT_EXPORT_VIEW_PROPERTY(stateMachineName, NSString)
1617
RCT_EXPORT_VIEW_PROPERTY(isUserHandlingErrors, BOOL)

src/Rive.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434
RiveAssetPropType,
3535
RiveRGBA,
3636
PropertyType,
37+
DataBindBy,
38+
AutoBind,
3739
} from './types';
3840
import { convertErrorFromNativeToRN, XOR } from './helpers';
3941

@@ -374,6 +376,7 @@ type RiveProps = {
374376
alignment: Alignment;
375377
artboardName?: string;
376378
referencedAssets?: FilesHandledMapping;
379+
dataBinding?: DataBindBy;
377380
animationName?: string;
378381
stateMachineName?: string;
379382
ref: any;
@@ -403,6 +406,7 @@ type Props = {
403406
* @experimental This is an experimental feature and may change without a major version update (breaking change).
404407
*/
405408
referencedAssets?: FilesHandledMapping;
409+
dataBinding?: DataBindBy;
406410
animationName?: string;
407411
stateMachineName?: string;
408412
autoplay?: boolean;
@@ -431,6 +435,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
431435
layoutScaleFactor,
432436
artboardName,
433437
referencedAssets: referencedAssets,
438+
dataBinding = AutoBind(false),
434439
animationName,
435440
stateMachineName,
436441
testID,
@@ -949,6 +954,7 @@ const RiveContainer = React.forwardRef<RiveRef, Props>(
949954
alignment={alignment}
950955
artboardName={artboardName}
951956
referencedAssets={convertedAssetHandledSources}
957+
dataBinding={dataBinding}
952958
animationName={animationName}
953959
stateMachineName={stateMachineName}
954960
/>

src/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,23 @@ export enum PropertyType {
206206
Trigger = 'trigger',
207207
Enum = 'enum',
208208
}
209+
210+
export type DataBindBy =
211+
| { type: 'autobind'; value: boolean }
212+
| { type: 'index'; value: number }
213+
| { type: 'name'; value: string }
214+
| { type: 'empty' };
215+
216+
export const AutoBind = (value: boolean): DataBindBy => ({
217+
type: 'autobind',
218+
value,
219+
});
220+
export const BindByIndex = (value: number): DataBindBy => ({
221+
type: 'index',
222+
value,
223+
});
224+
export const BindByName = (value: string): DataBindBy => ({
225+
type: 'name',
226+
value,
227+
});
228+
export const BindEmpty = (): DataBindBy => ({ type: 'empty' });

0 commit comments

Comments
 (0)