React native Image in two scrollView not showing - image

I started react-native and I am having a hard time finding how I can load a picture from an uri and show it into two scrollView (seems I need that, one for vertical and one for horizontal scrolls). I could make it works for a local file, but as soon as I get it from internet, the picture never show up except if I give a specific size.
<View style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ flex: 0, alignContent: "stretch" }}>
<ScrollView horizontal={true} contentContainerStyle={{ flex: 0, alignContent: "stretch" }}>
<Image
style={{ flex: 1, backgroundColor: 'white' }}
resizeMode='cover'
source={{ uri: 'https://cdn.pixabay.com/photo/2017/02/15/11/15/wintry-2068298_960_720.jpg' }}
/>
</ScrollView>
</ScrollView>
</View>
The same code without the scrollViews shows the image perfectly but with them the image never shows. I tried, the alignSelf:'stretch', or to set top/bottom/right/left to 0 and also width:'100%' and height:'100%' for the image nothing giving the expected result which is take all the space possible showing the image and be able to scroll inside it horizontally and vertically
Thanks for your help.

Finally found the answer :
var myUri = 'https://cdn.pixabay.com/photo/2017/02/15/11/15/wintry-2068298_960_720.jpg';
var mode = 'cover'
Image.getSize(myUri, (width, height) => { this.setState({ width, height }) });
return (
<ScrollView contentContainerStyle={{ flex: 0 }}>
<ScrollView horizontal={true} contentContainerStyle={{ flex: 0 }}>
<Image
style={{ width: width, height: height }}
resizeMode={mode}
source={{ uri: myUri }}
/>
</ScrollView>
</ScrollView>
);

Related

react native image loading is too slow

i get the 22 images from the react-native-image-filter.
this image are base64 format,
Here is the code on render runction.
render() {
const { images, imageIndex } = this.state;
return (
<View style={styles.container}>
{images?.length > 21 && <View style={{ flex: 1 }}>
<Image resizeMode={'contain'} source={{ uri: `data:image/png;base64,${images[imageIndex]?.base64}` }} style={{ width: 400, flex: 1 }} />
</View>}
<View style={{ height: 140, paddingVertical: 20 }}>
<ScrollView horizontal>
{images?.map((item, index) => {
return (
<View style={[{ marginHorizontal: 10 }, styles.center]}>
<Image
resizeMode={'cover'}
key={index.toString()}
onPress={() => this.setState({ imageIndex: index })}
source={{ uri: `data:image/png;base64,${item.base64}` }}
style={{ width: 80, height: 100, borderRadius: 8 }} />
</View>
)
})}
</ScrollView>
</View>
</View>
);
}
everything is good, but the image loading is too slow.
How can i increate the loading speed? Help me...
The problem is most definitely in Base64-encoded images.
The slowdown is because React Native has to pass quite long Base64 strings back and forth through the Native<->JS bridge, plus native platform has to parse Base64 and convert it into the correct memory representation for the native image view. Many optimizations provided by the platform are therefore missed.
Ideally, all image manipulations should happen on the native platform, and none of the actual image data should even touch JS.
I'd advise to try these two libraries for more performant filter implementations:
https://github.com/GregoryNative/react-native-gl-image-filters
https://github.com/iyegoroff/react-native-image-filter-kit

Image disappears on component re-renders

I have a functional component that shows a preview of the image or video being uploaded. I render this conditionally based off a state variable called media:
const renderMediaInPreview = useCallback(() => {
if(media && media.type === "image") {
return (
<Image style={{ height: '100%', aspectRatio: 1, resizeMode: 'cover'}} source={{ uri: media.uri }} />
)
} else if(media && media.type === 'video') {
return (
<Video
style={{ flex: 1 }}
source={{ uri: media.uri }}
isLooping
shouldPlay
resizeMode={Video.RESIZE_MODE_COVER}
isMuted={true}
/>
)
}
}, [media])
I've wrapped it in a useCallback so the function is only called when the media value changes. However using console logs I am able tto see that whenever the other state of the component is changed this re-runs and even though the media value stays the same it make the image disappear. Any insights? Below is my return and clip of behavior.
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAwareScrollView
style={{ flex: 1 }}
resetScrollToCoords={{ x: 0, y: 0 }}
contentContainerStyle={{ flex: 1, alignItems: 'center' }}
scrollEnabled={true}
keyboardShouldPersistTaps={'handled'}
behavior={Platform.OS == "ios" ? "padding" : "height"}
removeClippedSubviews={true}
>
{ renderErrorMessage() }
...
</KeyboardAwareScrollView>
</SafeAreaView>
);
https://drive.google.com/file/d/1-fYSOYTP0W86rRaS_CHqWw_gYfZFBcXT/view?usp=sharing
Was able to track this down and figured out it was not in relation to an issue with useCallback. I had done some refactoring of code and media was changing slightly that I didn't notice because of the refactor and no longer rendering anything because if didn't match the above if/then check.
Minor error that ended up taking a ton of unnecessary time... sigh.

react-navigation BottomTabNavigator VERY slow on some Android devices

I've got a minimal test ReactNative test in Expo running. 2 screens, both just have Text within a View.
This is run on a MBP, and tested on my Android device (Note10+) via QR code.
It is not just slow, it's unresponsive most of the time, when it does navigate to a tab on click then it's either instant (rarely) or takes 5-10 seconds. But usually nothing happens.
There are no errors being shown, replacing the same code with a Stack or Drawer works fine.
2 basic screens:
function StackScreenA0({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Test stack 0</Text>
<Button title="goto 1" onPress={() => navigation.navigate('Screen A1')} />
</View>
);
}
function StackScreenA1({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Test stack 0</Text>
<Button title="goto 0" onPress={() => navigation.navigate('Screen A0')} />
</View>
);
}
App code (minus imports):
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator initialRoutName='Screen A0'>
<Tab.Screen name="Screen A0" component={StackScreenA0} />
<Tab.Screen name="Screen A1" component={StackScreenA1} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;
I've never used Expo before but I can't imagine this is normal behaviour but cannot see a reason for it working so badly.
EDIT: I've found other devices to test and tabs are fine on iPad4, Galaxy9, but seriously bad on Note10+.
Rich

React-Native | ScrollView right to left

I've got a simple ScrollView:
<ScrollView
style={$style.category_container}
horizontal={true}
showsHorizontalScrollIndicator={false}
automaticallyAdjustContentInsets={true}
>
<Item title={'1'} />
<Item title={'2'} />
</ScrollView>
Item is a component that loads several thumbnails. My application is planned for both LTR and RTL users, so there is a change in directions for RTL.
The problem is when I'm using the RTL interface - the ScrollView is still moving from left to right, and I can't see all my thumbnails.
How can I solve it?
If someone will run into this in the future:
There isn't any 'built-in' property that will set ScrollView's direction to RTL at the moment.
However That's what worked for me:
set flexDirection: 'row-reverse' to ScrollView's style, which will order your items from right to left.
use onContentSizeChange to init list's scroll on the right side.
Here's an example:
scrollListToStart(contentWidth, contentHeight) {
if (I18nManager.isRTL) {
this.scrollView.scrollTo({x: contentWidth});
}
}
render() {
let containerStyle = I18nManager.isRTL ? styles.RTLContainer : styles.LTRContainer;
return (
<ScrollView
ref={ref => this.scrollView = ref}
onContentSizeChange={this.scrollListToStart.bind(this)}
horizontal={true}
style={[styles.buttonsContainer, containerStyle]}>
{this.renderButtons()}
</ScrollView>
)
}
const styles = StyleSheet.create({
RTLContainer: {
flexDirection: 'row-reverse'
},
LTRContainer: {
flexDirection: 'row'
}
})
you can use this way
i did this and worked for me
This solution has 2 rounds
1-first make this style for your scrollView : style={{scaleX:-1}}
2-second make this style for each of your childs in scrollView : style={{scaleX:-1}}
For Example
<ScrollView
horizontal={true}
contentContainerStyle={{height: 65}}
style={{scaleX:-1}}
showsHorizontalScrollIndicator={false}>
{
data.sports.map((data,index) => {
return(
<View key={index}
style={{width:150,height:55,backgroundColor:'yellow', marginHorizontal:4,scaleX:-1}}/>
)
})
}
</ScrollView>
As you can see my scrollview has scaleX = -1 style
Also all of my childs in scrollView has scaleX = -1 style
as scaleX is deprecated in views you can use transform:[{rotateY:'180deg'}] instead
Just in case someone has a similar problem to mine. I was doing a horizontal ScrollView with user images and needed the images to appear from right to left. Thank you Mr-Ash & Sergey Serduk for getting me there.
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={{
alignSelf: "center",
borderRadius: 100,
transform: [{ scaleX: -1 }],
}}>
{users.map((user, i) => {
return (
<View key={i} style={[{ transform: [{ scaleX: -1 }], zIndex: 100 - i }]}>
<UserImage leftMargin={-27} />
</View>
);
})}
<View style={{ marginRight: 27 }} />
</ScrollView>
In my case a proper solution is wrapping a <ScrollView/> component with <View/> see below:
<View style={{direction: 'rtl'}}><ScrollView ... /></View>
Direction can be set as rtl, ltr, inherit, initial, revert or unset
The best possible solution for RTL ScrollView is the combination of scrolling it to the end and flex-direction: 'row-reverse'
const scrollRef = useRef<any>();
const scrollToEnd = () => {
scrollRef.current.scrollToEnd({ animated: false });
};
<ScrollView
contentContainerStyle={{
flexDirection: "row-reverse"
}}
onContentSizeChange={scrollToEnd}
ref={scrollRefOne}
horizontal
showsHorizontalScrollIndicator={false}
>
...
</ScrollView>

ReactNative ListView inconsistent separator lines

On Android 4.4, ListView separator lines are inconsistent in thickness, and some do not render.
I can't see how this can be a code issue, this is how I render them:
separator: {
height: 1,
backgroundColor: 'grey',
}
...
<ListView
renderSeparator={(sectionID, rowID) =>
<View key={`${sectionID}-${rowID}`} style={styles.separator} />
}
.../>
Here is a screenshot of a View with this problem:
This issue does not happen on iOS or Android 6.
Anyone had this problem before?
Update
I did a test, this is not Android4 issue. It happens on all API version when running on Nexus One device (in android emulator)
I had this issue on iOS and worked around it by adding a hairline margin, like so:
<View
style={{
...styles,
borderWidth: StyleSheet.hairlineWidth,
margin: StyleSheet.hairlineWidth,
}}
>
{// ...row content}
</View>
Just give the height:hairlineWidth in style
I had the same issue and solved changing the view height from a number to StyleSheet.hairlineWidth as some folks said before. Trying to be more visual/specific:
Before:
renderItemSeparator() {
return (
<View style={{ height: .2, backgroundColor: 'rgba(0,0,0,0.3)' }} />
);
}
After:
renderItemSeparator() {
return (
<View style={{ height: StyleSheet.hairlineWidth, backgroundColor: 'rgba(0,0,0,0.3)' }} />
);
}
Actually there is no fix. It's RN "render-canvas-bug".
But I found hack solution.
<ListView
style={Style.listView}
dataSource={data}
renderRow={(data) => this._renderRow(data)}
/>`
Style.listView: {
backgroundColor: '#fff',
}, // or another backgroundColor you need
Then:
_renderRow(goods) {
return (
<View key={'goods_' + goods.id} style={Style.listView_item}>
<TouchableOpacity or View or ...
style={[Style.flex, Style.flexRow, Style.separatorRow, Style.u_paddingVerticalS, Style.u_middle]}
onPress={() => this._xyz(goods)}>
<View>
<AppFont>{goods.name}</AppFont>
</View>
</TouchableOpacity or View or ...>
</View>
);
}
Only important TouchableOpacity style is Style.separatorRow to render your separator. This style should be inside listView_item, where you can use another styles.
listView: {
backgroundColor: '#fff',
},
listView_item: {
paddingHorizontal: em(1.5),
},
flex: {
flex: 1,
},
flexRow: {
flexDirection: 'row',
},
separatorRow: {
marginBottom: 1,
borderBottomWidth: 1,
borderBottomColor: Colors.canvasColor,
},
You can use StyleSheet.hairlineWidth instead of 1 but it's not a must.
I reported it on GitHub
My workaround was to style the containing view and text like this:
const styles = StyleSheet.create({
rowViewContainer: {
flex: 1,
paddingRight: 15,
paddingTop: 13,
paddingBottom: 13,
borderBottomWidth: 0.5,
borderColor: '#c9c9c9',
flexDirection: 'row',
alignItems: 'center',
},
rowText: {
marginLeft: 15,
},
});
This is the ListView:
<ListView
dataSource={this.state.dataSource}
renderRow={(data) => <View style={styles.rowViewContainer}>
<Text style={styles.rowText}>
{data.bb_first_name}
</Text>
</View>}
/>
Looks nice:
This happens because you have empty rows in your data source. You can style your separators to see it
To avoid this just filter your data.
I faced the same issue when trying to render a Divider with a width of 0.5.
It rendered properly on devices with pixel ratio of 2 (e.g. iPhone SE 2nd gen.) but rendered random width on devices with pixel ratio of 3 (e.g. iPhone 12).
As suggested by other answers, using Stylesheet.hairlineWidth fixes the random width issue but the problem was that the width was thinner than 0.5 on devices with pixel ratio of 3.
So this fixed my problem:
import { PixelRatio, View } from 'react-native';
...
export const Divider = () => {
const width = PixelRatio.roundToNearestPixel(0.5);
...
return <View style={{ width }} ... />
}

Resources