1- import { StyleSheet , View , Text , TouchableOpacity } from 'react-native' ;
2- import { useState , useMemo } from 'react' ;
1+ import { StyleSheet , View , Text , TouchableOpacity , Button } from 'react-native' ;
2+ import { useState , useMemo , useRef , useEffect } from 'react' ;
33import type { Metadata } from '../helpers/metadata' ;
44import {
55 DataBindMode ,
66 RiveView ,
77 useRiveFile ,
88 type ViewModelInstance ,
9+ RiveImages ,
10+ type RiveViewRef ,
911} from '@rive-app/react-native' ;
1012
1113type BindModeOption =
@@ -77,6 +79,10 @@ export default function ManyViewModels() {
7779 require ( '../../assets/rive/many_viewmodels.riv' )
7880 ) ;
7981 const [ bindMode , setBindMode ] = useState < BindModeOption > ( 'none' ) ;
82+ const [ isLoadingImage , setIsLoadingImage ] = useState ( false ) ;
83+ const [ imageError , setImageError ] = useState < string | null > ( null ) ;
84+ const riveViewRef = useRef < RiveViewRef > ( undefined ) ;
85+ const isListening = useRef ( false ) ;
8086
8187 // Create a ViewModelInstance for "green" to demonstrate instance binding
8288 const greenInstance = useMemo ( ( ) => {
@@ -91,13 +97,69 @@ export default function ManyViewModels() {
9197 }
9298 } , [ riveFile ] ) ;
9399
100+ const handleLoadImage = async ( ) => {
101+ if ( ! riveViewRef . current ) return ;
102+
103+ setIsLoadingImage ( true ) ;
104+ setImageError ( null ) ;
105+ try {
106+ const vmi = riveViewRef . current . getViewModelInstance ( ) ;
107+ if ( ! vmi ) {
108+ console . log ( 'No ViewModelInstance found on RiveViewRef' ) ;
109+ setImageError ( 'No ViewModelInstance found on RiveViewRef' ) ;
110+ return null ;
111+ }
112+ const imgProp = vmi . imageProperty ( 'imageValue' ) ;
113+ if ( ! imgProp ) {
114+ setImageError ( 'Image property "imageValue" not found' ) ;
115+ return null ;
116+ }
117+
118+ if ( ! isListening . current ) {
119+ imgProp . addListener ( ( ) => {
120+ console . log ( '[IMAGE PROPERTY LISTENER]: Image property changed!' ) ;
121+ } ) ;
122+ isListening . current = true ;
123+ }
124+
125+ const riveImage = await RiveImages . loadFromURLAsync (
126+ 'https://picsum.photos/id/372/500/500'
127+ ) ;
128+ imgProp . set ( riveImage ) ;
129+ riveViewRef . current . play ( ) ;
130+ } catch ( err ) {
131+ const errorMsg = err instanceof Error ? err . message : 'Unknown error' ;
132+ setImageError ( errorMsg ) ;
133+ console . error ( 'Failed to load image:' , errorMsg ) ;
134+ } finally {
135+ setIsLoadingImage ( false ) ;
136+ }
137+ return true ;
138+ } ;
139+
94140 const dataBindValue = getDataBindValue ( bindMode , greenInstance ) ;
141+ useEffect ( ( ) => {
142+ isListening . current = false ;
143+ } , [ dataBindValue ] ) ;
95144
96145 return (
97146 < View style = { styles . container } >
98147 < BindModeSelector selectedMode = { bindMode } onModeChange = { setBindMode } />
148+ < View style = { styles . imageButtonContainer } >
149+ < Button
150+ title = { isLoadingImage ? 'Loading Image...' : 'Load Test Image' }
151+ onPress = { handleLoadImage }
152+ disabled = { isLoadingImage || ! riveFile }
153+ />
154+ { imageError && < Text style = { styles . errorText } > { imageError } </ Text > }
155+ </ View >
99156 { riveFile && (
100157 < RiveView
158+ hybridRef = { {
159+ f : ( ref ) => {
160+ riveViewRef . current = ref ;
161+ } ,
162+ } }
101163 style = { styles . rive }
102164 file = { riveFile }
103165 dataBind = { dataBindValue }
@@ -119,6 +181,17 @@ const styles = StyleSheet.create({
119181 flex : 1 ,
120182 backgroundColor : '#fff' ,
121183 } ,
184+ imageButtonContainer : {
185+ padding : 16 ,
186+ backgroundColor : '#fff' ,
187+ borderBottomWidth : 1 ,
188+ borderBottomColor : '#e0e0e0' ,
189+ } ,
190+ errorText : {
191+ color : 'red' ,
192+ marginTop : 8 ,
193+ fontSize : 12 ,
194+ } ,
122195 rive : {
123196 flex : 1 ,
124197 width : '100%' ,
0 commit comments