onLayout with Image - React Native - react-native

I have a problem with onLayout in Image:
I have a function to display the dimension.
measureView(event) {
console.log(event.nativeEvent.layout.width);
console.log(event.nativeEvent.layout.height);
}
This Works and shows me the dimension of the TouchableWithoutFeedback
<TouchableWithoutFeedback
style={styles.touchableWithoutFeedback}
onLayout={(event) => this.measureView(event)}>
<Image style={styles.image}
source={require("./../assets/images/photo_1.jpg")}/>
</TouchableWithoutFeedback>
But With the onLayout in the Image, i got nothing, just undefined.
<TouchableWithoutFeedback
style={styles.touchableWithoutFeedback}>
<Image style={styles.image}
source={require("./../assets/images/photo_1.jpg")}
onLayout={(event) => this.measureView(event)}/>
</TouchableWithoutFeedback>
Do you have an Idea why?
Thanks!

I had this problem recently as well. It looks like the <Image /> component mounts before the actual image gets loaded into the container. You probably want to instead use the onLoad property of <Image />. Once you do that you can pretty much proceed as normal...
<Image
onLoad={event => { console.log(event.nativeEvent.source); }}
source={require("./../assets/images/photo_1.jpg")}
style={styles.image}
/>
// Would log { height: 123, url: 'somestring', width: 123 }
Keep in mind that may or may not only work for static resources. I'm not 100% that network images provide a size through that function.
Good luck! :)

Related

React-Native Image Not Showing

Okay, so I have a react-native screen looking like
const SentimentScreen = ({ navigation, route }) => {
return (
<View style={styles.container}>
<View style={styles.container}>
<Text style={styles.titleText}>Sentiment: {route.params.sent}</Text>
<Image
style={{width: '100%', height: '50%'}}
source={{uri:route.params.url}}
/>
</View>
<StatusBar style="auto" />
</View>
);
};
And the url param is a string like 'https://www.example.com/lol.png'
But the image doesn't show. What am I doing wrong? (I'm still kind of a noob in terms of react-native)
Take out the image from path first give it specific, height , width and size .Then you can pass in uri.

Float an image zooming on an "instagram-like" news feed in react-native

I'm working on an "instagram-like" news feed for an existing react native (0.63.4) application. At this stage I'm using the react-native-gesture-handler (1.10) to handle the pinch/zoom functionality using its PinchGestureHandler and FlatList implementations.
I have an outer vertical flatlist for scrolling the new items and, for each item, an inner horizontal flat list to scroll the images left and right.
If I scale the image inline then it works fine, though it's a little slow due to having to re-layout all the other items.
What I'd like to do is "float" the image above everything else and then snap back after zooming. I can do all of that, except floating the image. I've tried setting overflow:'visible' on the entire tree, but it doesn't help zIndex and elevation also don't seem to help.
Visually I'd like to (kind of) pop the image out while zooming, then it can snap back at the end. How do I do this?
--Implementation details:
The feed is just an array of NewsItem:
interface NewsItem {
id: string,
title: string,
images: NewsImage[],
datePublished: Date
}
interface NewsImage {
id: string,
uri: string,
width: number,
height: number,
ref: RefObject<PinchGestureHandler>
}
imageRefs is a flattened list of references for all the images.
The main layout is like this (slightly simplified):
<View>
<FlatList
waitFor={imageRefs}
data={newsFeed}
keyExtractor={item => item.id}
renderItem={({item}) => <NewsItem item={item} /> } />
</View>
NewsItem
<View>
<Text>{props.item.title}</Text>
<FlatList
horizontal={true}
waitFor={props.item.images.map(img => img.ref)}
data={props.item.images}
keyExtractor={img => img.id}
renderItem={({item})} => <NewsImage info={item} />
/>
</View>
NewsImage has all the code to handle the pinch/zoom transform stored in the variables:
scale
scaledWidth
scaledHeight
xScaledTranslate
yScaledTranslate
With the layout:
<View style={{
width: info.width * minScale,
height: info.height * minScale,
position: 'relative',
}}>
<Animated.View style={{
position:'absolute',
overflow:'visible',
width:scaledWidth,
height:scaledHeight,
}}>
<PinchGestureHandler
onGestureEvent={pinchGestureEventHandler}
onHandlerStateChange={pinchStateEventHandler}
ref={ref}
>
<Animated.Image
defaultSource={require('../Shared/assets/logos/bootsplash_logo.png')}
source={info}
style={{
width: info.width,
height: info.height,
overflow:"visible",
transform: [
{translateX: xScaledTranslate},
{translateY: YScaledTranslate},
{scale: scale},
]
}}
resizeMode='cover'
/>
</PinchGestureHandler>
</Animated.View>
</View>
I dunno did you solved this issue, but simply you can't do that with FlatList.
I had the similar issue and solved it to converting my FlatList to a ScrollView
You can use this: https://github.com/postillonmedia/react-native-instagram-zoomable
It's a JS implementation so you can also modify the code as needed. It zooms the view/image/component above everything on the screen. It creates a clone of the element and uses PanGestures to zoom

React Native onLongPress triggered onPress if pressed component is re-rendered

I am implementing an image gallery. There's a list of squared images, if user longPress an image component it will re-render to a selected image component which show a blur background and a tick on top of the image.
Here's the normal image component:
<TouchableOpacity
key={index}
onPress={this.normalModeImgClick(img, index)}
onLongPress={this.startSelectMode(index)}
>
<Image style={styles.img} source={{uri: img.resized_xs_url}}/>
</TouchableOpacity>
Here's the component at select mode:
<TouchableOpacity
key={itemIndex}
style={styles.selectedImgWrapper}
onPress={this.selectModeImgClick(imgItem, itemIndex)}
>
<Image style={styles.img} source={{uri: imgItem.img.resized_xs_url}}/>
{imgItem.selected &&
<View style={styles.selectedImgCover}>
<Image style={styles.selectedIcon} source={require('../../assets/icon_tick.png')}/>
</View>
}
</TouchableOpacity>
As you can see, if you longPress a normal image component startSelectMode will trigger, and that image will re-render and turned to a select mode component. However, selectModeImgClick will also be triggered which it is not supposed to (as the user is still doing the longPress action).
How to prevent this happening?
Accidentally discovered an alternative right after I posted the question, solved by adding an empty onLongPress function props to the selected mode component like this:
<TouchableOpacity
key={itemIndex}
style={styles.selectedImgWrapper}
onPress={this.selectModeImgClick(imgItem, itemIndex)}
// Override 'onLongPress' with an empty function
onLongPress={() => {}}
>
<Image style={styles.img} source={{uri: imgItem.img.resized_xs_url}}/>
{imgItem.selected &&
<View style={styles.selectedImgCover}>
<Image style={styles.selectedIcon} source={require('../../assets/icon_tick.png')}/>
</View>
}
</TouchableOpacity>
Since the component is not designed to have a longPress action, I'm looking forward to better solutions:

React Native: print full image in a Scrollview

I have an image that is very tall, but not very large. I want to print it in a scrollview, so you can see it all on one screen, but nothing works.
after playing with widths and heights and flexs for my scrollview and my image, the best result I get is when I have on of them with style={{width='100%'}}, like that
<ScrollView style={{width: '100%'}}>
<Image
source={require('./assets/skeleton/fullSkeleton.png')}
resizeMode='cover'
/>
</ScrollView>
which prints the image at full width, but the scrollview's height is the original image's height, which doesn't let me see the whole resized image.
Here, a bad drawing to represent my image, and what I want to see on my phone screen
EDIT:
With this bit of code:
<ScrollView
contentContainerStyle={{alignItems: 'center'}}>
<Image
source={require('./fullSkeleton.png')}
resizeMode='cover'
/>
</ScrollView>
I end up with
Which can be scrolled down to see the rest of the Image. As you can see, it can be navigated vertically, but doesn't take the whole screen's width
This kind of thing in Reat Native is actually quite tricky. But you can do it easily; First get picture dimensions:
const FullWidthPicture = ({ uri }) => {
const [ratio, setRatio] = useState(1);
useEffect(() => {
if (uri) {
Image.getSize(uri, (width, height) => {
setRatio(width / height);
});
}
}, [uri]);
return (
<Image
style={{ width: '100%', height: undefined, aspectRatio: ratio }}
resizeMode="contain"
source={{ uri }}
/>
);
};
And use it like this:
<ScrollView style={{flex: 1}}>
<FulWidthImage uri={pictureUri} />
</ScrollView>
The following may help:
You can use contentContainerStyle to style the contents of the ScrollView. Like this (essentially what you have):
<ScrollView
contentContainerStyle={{alignItems: 'center'}}>
<Image
source={require('./fullSkeleton.png')}
resizeMode='cover'
/>
</ScrollView>
That works for me - I can scroll down the whole image and at the bottom it stays visible.
NB: To make the image cover the whole screen while maintaining the height required, you will need to set the width specifically. In my case on iPhone 7+ that meant adding style={{width: 414}} to the Image component AND changing resizeMode to 'stretch'.
Here is the official take on Image resizing...and getting device dimensions will be useful here too.

React Native local image loads a second after content

I have a local image that I am using as a background image. It loads around a second after the rest of the content.
<Image source={require('./assets/climbing_mountain.jpeg')} style={styles.imageContainer}>
imageContainer: {
flex: 1,
width: null,
height: null,
}
Is there a way to load the content after the image is loaded? I tried using the onLoad and onLoadEnd props, but they were fired prior to the image loading.
Yes there is.. it's not that simple actually, you will need to use Image prefetch.. check this link : React Image prefetch
Or actually you can try this:
<Image
onLoadEnd={()=>this.setState({loadEnd:true})}
source={require('./assets/climbing_mountain.jpeg')}
style={styles.imageContainer}>
and in the render method you can do something like which I don't really recommend:
render(){
if(!this.state.loadEnd) {
return(
<Image
key='image_key'
onLoadEnd={()=>this.setState({loadEnd:true})}
source={require('./assets/climbing_mountain.jpeg')}
style={styles.imageContainer}>
)
}else {
return (
<View>
....
<Image
key='image_key'
onLoadEnd={()=>this.setState({loadEnd:true})}
source={require('./assets/climbing_mountain.jpeg')}
style={styles.imageContainer}>
</view>
....
)
}
}