React-Native | ScrollView right to left - uiscrollview

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>

Related

React Native - Fit image to screen

I just want to fit an image of any size to the phone screen, so it just stays there as a background. I have the same issue with a logo I'm trying to put in the footer, I can't get it to fit in the it's view container.
I've tried many solutions I found in similar questions, using resizeMode and many width/height values, but nothing seems to work. My image is always displayed the same way.
Code for the image component:
import React from 'react';
import { View, Image } from 'react-native';
const Workspace = (props) => {
return (
<View
style = {styles.workspaceStyle}>
<Image
source={props.img}
resizeMode = 'contain'/>
{props.children}
</View>
);
};
const styles = {
workspaceStyle: {
flex: 1
}
}
export default Workspace;
My app.js render and style code:
render() {
return (
<View style = {{flex: 1}}>
<Workspace
img={require('./images/quarto.png')}/>
<ScrollView>
<Header>
<HeaderItem img={require('./images/camera.png')}/>
<HeaderItem img={require('./images/camera.png')}/>
<HeaderItem img={require('./images/camera.png')}/>
<HeaderItem img={require('./images/camera.png')}/>
</Header>
</ScrollView>
<ScrollView style = {{flexDirection: 'row'}}>
{this.sideMenuShow()}
</ScrollView>
<Footer>
<View style = {styles.logoContainerStyle}>
<Image
style = {styles.logoStyle}
source = {require('./images/magicalStage.png')}
resizeMethod = "scale"
/>
</View>
<Text style = {{color: 'white', marginTop: 5, marginBottom: 2}}>teste, teste, teste, teste</Text>
</Footer>
</View>
);
}
}
const styles = {
logoContainerStyle: {
marginRight: 5,
marginLeft: 5,
marginTop: 2,
marginBottom: 3,
width: "20%"
},
logoStyle: {
paddingLeft: 2,
paddingRight: 2
}
}
Thanks in advance!
EDIT:
In app.js, your outer view need to use width and height of the screen:
width: Dimensions.get('window').width,
height: Dimensions.get('window').height
Next, in Workspace: use stretch instead of contain ( same for your footer, add resizeMode )
resizeMode: 'stretch',
I do mine like this:
BackgroundImageStyle.js
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
container: {
flex: 1,
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
},
image: {
flex: 1,
resizeMode: 'cover',
}
})
BacgroundImage.js
import React, { Component } from 'react'
import { View, Image } from 'react-native'
import styles from './BackgroundImageStyle'
export default class BackgroundImage extends Component {
render() {
return (
<View style={styles.container} >
<Image
style={styles.image}
source={this.props.source}
/>
</View>
)
}
}
then you can use it like
<BackgroundImage source={your_image}/>
I hope everything is clear, the trick is to set position absolute and then top, left, bottom, and right to 0
You can use the ImageBackground component from react-native
https://facebook.github.io/react-native/docs/imagebackground
return (
<ImageBackground source={...} style={{width: '100%', height: '100%'}}>
<Text>Inside</Text>
</ImageBackground>
);

React Native: how to animate Views from being stacked vertically to horizontally

So, essentially, I have two Views stacked vertically to start and on a button press they need to be animated so the first View scoots to the left and the second View goes up and sits to the right of the first one. Seems easy enough to do with adjusting flex on the first View and changing top and left styling on the second View, looks great on IOS, however on Android this is causing problems with the second View being cut off on the bottom when it goes up (presumably because the View's flex or height isn't tall enough for the content, but unfortunately I can't change that because it messes up flex styling for the rest of the content beneath).
Any suggestions on how to approach this?
This may not be possible, but I'm wondering if there is a way, either with LayoutAnimation or the Animated API, to animate these Views from a flexDirection 'column' to 'row'? That would be crazy convenient.
LayoutAnimation is wonderful, folks! Simply toggle between 'column' and 'row' on state change as desired and LayoutAnimation takes care of the rest—super neat. Here's a simple example for anyone else with the same problem, hope it helps:
export default class Test extends Component {
constructor() {
super();
this.state = {
toggleStyle: 'column',
}
if(Platform.OS === 'android'){
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
_onPress = () => {
LayoutAnimation.configureNext(CustomLayoutAnimation.position);
let direction = this.state.toggleStyle === 'row' ? 'column' : 'row'
this.setState({toggleStyle: direction})
}
render() {
return(
<View style={{flex: 1}}>
<View style={{flex: 1}} />
<View style={{flex: 1, flexDirection: this.state.toggleStyle}}>
<View style={{flex: 1, width:'100%', backgroundColor: 'blue'}} />
<View style={{flex: 1, width:'100%', backgroundColor: 'green', justifyContent: 'center', alignItems: 'center'}}><Text onPress={() => this._onPress()}>Toggle</Text></View>
</View>
<View style={{flex: 1}} />
</View>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Modal not displaying in react-native?

Hey guys i am trying to show modal on click of map marker but i fail to see any modal on marker click can anybody help me out what is the issue. I am using Modal from https://github.com/maxs15/react-native-modalbox
Here is the code:
import Modal from "react-native-modalbox";
export default class App {
openModal4(id) {
this.refs.modal4.open();
}
render() {
return (
<View style={style.mainContainer}>
<MapView
ref="map"
showUserLocation={true}
region={this.state.region}
onRegionChange={this.onRegionChange}
onRegionChangeComplete={this.onRegionChangeComplete}
style={styles.map}
rotateEnabled={false}
showsCompass={false}
>
{this.state.markers.map(function(marker) {
return (
<MapView.Marker
coordinate={marker.latlng}
key={marker.id}
onPress={this.openModal4}
/>
);
})}
</MapView>
<View
style={{
position: "absolute",
width: windowsWidth,
height: windowsHeight - 100,
alignItems: "center",
justifyContent: "center"
}}
>
<Image source={require("./assets/map-marker.png")} />
</View>
<Modal
style={[styles.modal, styles.modal4]}
position={"bottom"}
ref={"modal4"}
/>
</View>
);
}
}
can anybody help me out? Thanks in advance.
You should make changes, to bind this in callbacks
read this article Don't Use Bind When Passing Props
openModal4 = (id) => {
this.refs.modal4.open();
}
Guys Binding was the issue which gets solved after using arrow function =>
here is the code
openModal4(id){
this.refs.modal4.open();
}
{this.state.markers.map((marker) => {
return(
<MapView.Marker coordinate={marker.latlng} key={marker.id} onPress={this.openModal4} />
);
})}
And you need to bind the function in constructor as it consist of 'this'
this.openModal4 = this.openModal4.bind(this)
hope my struggle would help others

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 }} ... />
}

Image preloading in React Native

I am building my first app with React Native, an app with a long list of images. I want to show a spinner instead of image while image is loading. It is sounds trivial but i didn't found a solution.
I think for a spinner i suppose to use ActivityIndicatorIOS , but how am i combining it with an Image component?
<Image source={...}>
<ActivityIndicatorIOS />
</Image>
Is this a right direction? Am i missing something?
I will share my solution
<View>
<Image source={{uri: this.state.avatar}} style={styles.maybeRenderImage}
resizeMode={"contain"} onLoadStart={() => this.setState({loading: true})}
onLoadEnd={() => {
this.setState({loading: false})
}}/>
{this.state.loading && <LoadingView/>}
</View>
LoadingView.js
export default class LoadingView extends Component {
render() {
return (
<View style={styles.container}>
<ActivityIndicator size="small" color="#FFD700"/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
position: "absolute",
left: 0,
right: 0,
top: 0,
bottom: 0,
opacity: 0.7,
backgroundColor: "black",
justifyContent: "center",
alignItems: "center",
}
});
Here is a complete solution to providing a custom image component with a loading activity indicator centered underneath the image:
import React, { Component } from 'react';
import { StyleSheet, View, Image, ActivityIndicator } from 'react-native';
export default class LoadableImage extends Component {
state = {
loading: true
}
render() {
const { url } = this.props
return (
<View style={styles.container}>
<Image
onLoadEnd={this._onLoadEnd}
source={{ uri: url }}
/>
<ActivityIndicator
style={styles.activityIndicator}
animating={this.state.loading}
/>
</View>
)
}
_onLoadEnd = () => {
this.setState({
loading: false
})
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
activityIndicator: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
}
})
I will share my own solution based only on CSS manipulation, which in my opinion is easy to understand, and the code is pretty clean. The solution is a little similar to other answers, but doesn't require absolute position of any component, or creating any additional components.
The idea is to switch between showing an <Image> and <ActivityIndicator>, based on some state variable (isImageLoaded in the snippet below).
<View>
<Image source={...}
onLoad={ () => this.setState({ isImageLoaded: true }) }
style={[styles.image, { display: (this.state.isImageLoaded ? 'flex' : 'none') }]}
/>
<ActivityIndicator
style={{ display: (this.state.isImageLoaded ? 'none' : 'flex') }}
/>
</View>
Also you should set image size using flex property (otherwise image will be invisible):
const styles = StyleSheet.create({
image: {
flex: 1,
}
});
Note that you don't have to initiate the isImageLoaded variable to false in the constructor, because it will have undefined value and the if conditions will act as expected.
Just ran into the same issue. So basically you have the correct approach, but the indicator should of course only be rendered when the image is loading. To keep track of that you need state. To keep it simple we assume you have just on image in the component an keep the state for it in the same component. (The cool kids will argue you should use a higher order component for that and then pass the state in via a prop ;)
The idea then is, that your image starts out loading and onLoadEnd (or onLoad, but then the spinner gets stuck on error, which is fine or course) you re-set the state.
getInitialState: function(){ return { loading: true }}
render: function(){
<Image source={...} onLoadEnd={ ()=>{ this.setState({ loading: false }) }>
<ActivityIndicatorIOS animating={ this.state.loading }/>
</Image>
}
You could also start out with { loading: false } and set it true onLoadStart, but I'm not sure what the benefit would be of that.
Also for styling reasons, depending on your layout, you might need to put the indicator in a container view that is absolutely positioned. But you get the idea.
Yes, deafultSource and loadingIndicatorSource is not working properly. Also image component cannot contain children. Try this solutions => https://stackoverflow.com/a/62510268/11302100
You can simply just add a placeholder
import { Image } from 'react-native-elements';
<Image
style={styles(colors).thumbnail}
source={{ uri: event.image }}
PlaceholderContent={<ActivityIndicator color={colors.indicator} />}
/>
const [imageLoading, setIsImageLoading] = useState(true);
<View>
<View
style={[
{justifyContent: 'center', alignItems: 'center'},
imageLoading ? {display: 'flex'} : {display: 'none'},
]}>
<ActivityIndicator />
</View>
<Image
source={{uri: ""}}
onLoadEnd={() => {setIsImageLoading(false)}}
style={[
imageStyle,
imageLoading ? {display: 'none'} : {display: 'flex'},
]}
/>
</View>

Resources