diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt b/android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt index 72b3d59..01c006e 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt @@ -56,4 +56,13 @@ class HybridViewModelInstance(val viewModelInstance: ViewModelInstance) : Hybrid override fun artboardProperty(path: String) = getPropertyOrNull { HybridViewModelArtboardProperty(viewModelInstance.getArtboardProperty(path)) } + + override fun viewModel(path: String) = getPropertyOrNull { + HybridViewModelInstance(viewModelInstance.getInstanceProperty(path)) + } + + override fun replaceViewModel(path: String, instance: HybridViewModelInstanceSpec) { + val nativeInstance = (instance as HybridViewModelInstance).viewModelInstance + viewModelInstance.setInstanceProperty(path, nativeInstance) + } } diff --git a/example/assets/rive/viewmodelproperty.riv b/example/assets/rive/viewmodelproperty.riv new file mode 100644 index 0000000..b487a80 Binary files /dev/null and b/example/assets/rive/viewmodelproperty.riv differ diff --git a/example/src/pages/NestedViewModelExample.tsx b/example/src/pages/NestedViewModelExample.tsx new file mode 100644 index 0000000..aae1264 --- /dev/null +++ b/example/src/pages/NestedViewModelExample.tsx @@ -0,0 +1,251 @@ +import { + View, + Text, + StyleSheet, + ActivityIndicator, + Button, + TextInput, +} from 'react-native'; +import { useMemo, useRef, useState } from 'react'; +import { + Fit, + RiveView, + useRiveFile, + useRiveString, + type ViewModelInstance, + type RiveFile, + type RiveViewRef, +} from '@rive-app/react-native'; +import { type Metadata } from '../helpers/metadata'; + +export default function NestedViewModelExample() { + const { riveFile, isLoading, error } = useRiveFile( + require('../../assets/rive/viewmodelproperty.riv') + ); + + return ( + + {isLoading ? ( + + ) : riveFile ? ( + + ) : ( + {error || 'Unexpected error'} + )} + + ); +} + +function WithViewModelSetup({ file }: { file: RiveFile }) { + const viewModel = useMemo(() => file.defaultArtboardViewModel(), [file]); + const instance = useMemo( + () => viewModel?.createDefaultInstance(), + [viewModel] + ); + + if (!instance || !viewModel) { + return ( + + {!viewModel + ? 'No view model found' + : 'Failed to create view model instance'} + + ); + } + + return ; +} + +function ReplaceViewModelTest({ + instance, + file, +}: { + instance: ViewModelInstance; + file: RiveFile; +}) { + const riveRef = useRef(null); + const [replaced, setReplaced] = useState(false); + const [log, setLog] = useState([]); + + const { value: vm1Name, setValue: setVm1Name } = useRiveString( + 'vm1/name', + instance + ); + const { value: vm2Name, setValue: setVm2Name } = useRiveString( + 'vm2/name', + instance + ); + + const handleSetVm1Name = (newValue: string) => { + setVm1Name(newValue); + riveRef.current?.playIfNeeded(); + }; + + const handleSetVm2Name = (newValue: string) => { + setVm2Name(newValue); + riveRef.current?.playIfNeeded(); + }; + + const addLog = (msg: string) => setLog((prev) => [...prev, msg]); + + const handleReplace = () => { + // Get vm2's instance + const vm2Instance = instance.viewModel('vm2'); + if (!vm2Instance) { + addLog('❌ viewModel("vm2") returned undefined'); + return; + } + addLog(`✅ Got vm2 instance: ${vm2Instance.instanceName}`); + + // Replace vm1 with vm2's instance + try { + instance.replaceViewModel('vm1', vm2Instance); + addLog('✅ replaceViewModel("vm1", vm2Instance) succeeded'); + // Call playIfNeeded to update the graphics + riveRef.current?.playIfNeeded(); + addLog('✅ Called playIfNeeded() to refresh display'); + addLog('→ Now vm1 and vm2 point to the same instance'); + addLog('→ Changing vm2.name should also change vm1.name'); + setReplaced(true); + } catch (error) { + addLog(`❌ replaceViewModel failed: ${error}`); + } + }; + + return ( + + (riveRef.current = ref) }} + style={styles.rive} + autoPlay={true} + dataBind={instance} + fit={Fit.Contain} + file={file} + /> + + + replaceViewModel() Test + + Replace vm1 with vm2's instance. After replacement, changing vm2.name + should also change vm1.name since they share the same instance. + + + + vm1.name: + + + + + vm2.name: + + + + {!replaced && ( +