I have a "fullscreen" background image that is used for a page:
container = {
flex: 1,
width: null,
height: null
}
<View ...>
...
<Image ... styles={container}>
...
<TextInput ... />
...
</Image>
</View>
However, as you may notice, tapping on the text input will open up the keyboard and the height of view changes. Since the image is set to cover, it also adjusts as the dimension of the view changes. I want the height of the parent view and the <Image> to not be affected by the keyboard, and only the content of the <Image> should be pushed up by the keyboard.
I'm aware there is a <KeyboardAvoidingView> available but I am not sure how to use it or does it even handle this situation.
Any ideas would be great. Thanks.
I do like this in React Native and it works :
backgroundImage: {
position: 'absolute',
left: 0,
top: 0,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
I added
android:windowSoftInputMode="adjustPan"
to my AndroidManifest.xml and it worked out perfectly - the view doesn't get shrinked and the text inputs got pushed up.
Here's the solution I found to the same problem that I've faced. As you said, you can use react-native-keyboard-avoiding-view which is a really good way of avoiding keyboard and this solution implements that.
So what we have here is an image with style imageStyle wrapping everything.
render() {
return(
<Image source={{uri: 'blabla'}} style={imageStyle}>
<View style={styles.container}>
<KeyboardAwareScrollView>
<TouchableOpacity onPress={this.abc.bind(this)}>
<View style={styles.abc}>
<Text>Test</Text>
</View>
</TouchableOpacity>
...
</KeyboardAwareScrollView>
...
</View>
</Image>
)
}
and imageStyle:
const imageStyle = {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
resizeMode: 'stretch',
}
Bonus: If you are going to support screen rotations, you can do:
const { width, height } = Dimensions.get('window')
const imageStyle = {
width: width < height ? width : height,
height: width < height ? height : width,
resizeMode: 'stretch',
}
Change
android:windowSoftInputMode="adjustResize"
To
android:windowSoftInputMode="adjustPan"
In android/app/src/main/AndroidManifest.xml, block activity
I wanted to accomplish the same thing, but without changing windowSoftInputMode.
I was able to do it by setting just the height of the image to Dimensions.get('window').height.
My background image stays put when the keyboard opens, but the components sitting on top of it move out of the way.
Because I was using React Navigation also, I was having issues using the window height effectively. I have a notification stuck to the bottom, and it was off the screen. My eventual solution was to close the ImageBackground element prior to the children:
<View style={styles.container}>
<ImageBackground
source={BACKGROUND}
imageStyle={{ resizeMode: 'repeat' }}
style={styles.imageBackground}
/>
<SafeAreaView style={{ flex: 1, justifyContent: 'space-between' }}>
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? "padding" : "height"} style={{flex: 1, justifyContent: 'space-between'}}>
{children}
</KeyboardAvoidingView>
<Notification/>
</SafeAreaView>
</View>
With styles looking like
export const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
export const styles = StyleSheet.create(
{
notification: { position: 'absolute', bottom: 0, left: 0, right: 0, alignItems: 'stretch'},
imageBackground: { position: 'absolute', left: 0, top: 0, height: screenHeight, width: screenWidth },
container: {
flex: 1,
alignItems: 'stretch',
justifyContent: 'space-around',
},
});
Related
I have a TextInput that when pressed gets covered by the keyboard. So I wrapped it in a KeyboardAvoidingView. But regardless of the behavior that I set for this view, the TextInput won't move above the keyboard. Using position as the behavior moves the TextInput but only half way above the keyboard, while the other two don't seem to work at all.
I also tried wrapping my entire component with a KeyboardAvoidingView, but doing so breaks the entire layout.
Can anyone help me? I never managed to get KeyboardAvoidingView to work for me and now I really need it. Thanks in advance!
Here is my component. Also worth mentioning is that this component is top level(well, almost top level since it's wrapped in a Router)
const { height, width } = Dimensions.get('screen')
const style = StyleSheet.create({
main: {
height,
width,
flexDirection: 'column',
},
iconSelecter: {
width,
height: 196,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.primary,
marginTop: 32
},
icon: {
height: 164,
width: 164,
},
saveButton: {
width: 96,
height: 96,
borderRadius: 100,
backgroundColor: Colors.secondary,
alignItems: "center",
justifyContent: "center",
alignSelf: 'center',
position: 'absolute',
bottom: 96 + 32
},
saveIcon: {
height: 54,
width: 54,
},
textInputWrapper: {
borderBottomColor: Colors.textInputBorder,
width: 288,
borderBottomWidth: 1,
alignSelf: 'center',
marginTop: 96,
height: 48,
},
textInput: {
fontWeight: "300",
fontSize: 14,
margin: 0
},
hintWrapper: {
alignSelf: 'center',
marginTop: 4
},
hint: {
fontSize: 12,
fontFamily: "Roboto-Thin",
fontStyle: 'normal',
}
})
const CreateActivity = ({ goBack }: NavigationProps) => {
//////////////////////////////
//State and logic
///////////////
return (
// TODO: Add touchable opacity to dismiss keyboard
<View style={style.main}>
<Appbar title="New activity" canGoBack goBack={goBack} />
<View style={{ flex: 1 }}>
<View style={style.iconSelecter}>
<GestureRecognizer onSwipeLeft={nextIcon} onSwipeRight={lastIcon}>
<Image style={style.icon} source={icons[currentIconIndex]?.file}></Image>
</GestureRecognizer>
</View>
<View style={style.hintWrapper}>
<Text style={style.hint}>Swipe to cycle through the icons</Text>
</View>
<KeyboardAvoidingView>
<View style={style.textInputWrapper}>
<TextInput style={style.textInput} placeholder={"Give this activity a name"} value={name} onChangeText={setName}></TextInput>
</View>
</KeyboardAvoidingView>
<TouchableNativeFeedback onPress={createActivity} background={TouchableNativeFeedback.Ripple("#fff", true)}>
<View style={style.saveButton}>
<Image style={style.saveIcon} source={require("../../assets/icons/light/save.png")}></Image>
</View>
</TouchableNativeFeedback>
</View>
</View>
)
}
export default CreateActivity;
I suggest that you to try wrap all the content of the screen in <KeyboardAvoidingView /> (or make it one of the outermost elements), otherwise it only will slide up its children (the View and the TextInput) leaving the rest of the content in its original position, making the layout look overlaped and weird. If you do that, the value "position" should work fine.
Something like this:
<View style={style.main}>
<Appbar title="New activity" canGoBack goBack={goBack} />
<KeyboardAvoidingView behavior="position" >
<View style={{ flex: 1 }}> // --> Remove flex: 1 if you experience some issue with the positioning
<View style={style.iconSelecter}>
<GestureRecognizer onSwipeLeft={nextIcon} onSwipeRight={lastIcon}>
<Image style={style.icon} source={icons[currentIconIndex]?.file}></Image>
</GestureRecognizer>
</View>
<View style={style.hintWrapper}>
<Text style={style.hint}>Swipe to cycle through the icons</Text>
</View>
<KeyboardAvoidingView>
<View style={style.textInputWrapper}>
<TextInput style={style.textInput} placeholder={"Give this activity a name"} value={name} onChangeText={setName}></TextInput>
</View>
</KeyboardAvoidingView>
<TouchableNativeFeedback onPress={createActivity} background={TouchableNativeFeedback.Ripple("#fff", true)}>
<View style={style.saveButton}>
<Image style={style.saveIcon} source={require("../../assets/icons/light/save.png")}></Image>
</View>
</TouchableNativeFeedback>
</View>
</KeyboardAvoidingView>
</View>
Also see the comment in the code above. Check if you really need to use of flex: 1 in all the outer wrapper elements, and take a look to the height you are setting in the style.main based on dimentions. I don't think that it is necesary and I think it could lead to some measure issues if you fix the height of the parent container.
EDIT:
I was just digging in react-native docs and I realize that there is a zIndex that you could use to avoid ablsolute positioning. It is a relative style prop so it needs to be set between sibling views, like this:
export default class MyComponent extends React.Component {
render() {
return (
<View>
<View style={[styles.appbarShape, styles.appbarZIndex]} ><Text>Header</Text></View>
<KeyboardAvoidingView behavior="position" style={styles.contentZIndex}>
{other children}
<TextInput placeholder="enter text"/>
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
appbarShape: {
height: 80,
width: Dimensions.get('window').width,
justifyContent: 'center',
alignSelf: "stretch",
backgroundColor: "#FFF"
},
appbarZIndex: {
zIndex: 3,
},
contentZIndex: {
zIndex: 0
}
});
Since the view that represents the appbar has a greater zIndex it shows up over the ones with a lower zIndex
Check this out working in this snack https://snack.expo.io/5VXAcw4Y0
Docs: https://reactnative.dev/docs/layout-props
Hope it helps!
Use react-native-keyboard-aware-scroll-view
<KeyboardAwareScrollView extraHeight={135} enabledOnAndroid={true}
extraScrollHeight={70} style={styles.mainContainer}
automaticallyAdjustContentInsets={true}
enableOnAndroid={true}
keyboardShouldPersistTaps='handled'
scrollEnabled={true} >
//your form
</KeyboardAwareScrollView>
const styles = StyleSheet.create({
mainContainer: { flex: 1, marginHorizontal: 15, marginVertical: 15 },
});
I am trying to place a floating action button in the lower right corner of my app but it is placing it in the top left way off screen.
Returned view:
<View>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity onPress={this.onPress} activeOpacity={.5} >
<Image
source={require('./assets/images/hamburger.png')}
style={{ width: 30, height: 25, marginLeft: 15}}
/>
</TouchableOpacity>
</View>
<FloatingAction style={styles.bottom}/>
</View>
Styles:
const styles = StyleSheet.create({
bottom: {
flex: 1,
position: 'absolute',
bottom: 10,
right:10
},
});
My current view displays a header and a bottom tab view. I am able to place multiple FAB's in each tab screen but that produces an undesirable behavior. Thank you for any help.
Edit:
What I have:
What I want:
Your issue was on adding { flex: 1, position: 'absolute',} to the button style together. The parent component that covers all the phone screen would use flex: 1, your button component is the one that receives the style for the position.
Always creating a new component makes stuff easier to read and understand. So let's say you have a button component (<FloatingButton/>), you would do something like this:
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import FloatingButton from './FloatingButton';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>
I'm just a Text
</Text>
<FloatingButton
style={styles.floatinBtn}
onPress={() => alert(`I'm being clicked!`)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
floatinBtn: {
position: 'absolute',
bottom: 10,
right: 10,
}
});
You will get this result:
This is the button component:
import React from 'react';
import { View, TouchableOpacity } from 'react-native';
export default props => (
<TouchableOpacity onPress={props.onPress} style={props.style}>
<View
style={{
backgroundColor: 'blue',
width: 45,
height: 45,
borderRadius: 45,
}}
/>
</TouchableOpacity>
);
Check the snack demo: https://snack.expo.io/#abranhe/floating-btn
// this should occupy the whole screen
<View style={{flex:1}}>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity onPress={this.onPress} activeOpacity={.5} >
<Image
source={require('./assets/images/hamburger.png')}
style={{ width: 30, height: 25, marginLeft: 15}}
/>
</TouchableOpacity>
</View>
<FloatingAction style={styles.bottom}/>
</View>
const styles = StyleSheet.create({
bottom: {
position: 'absolute',
bottom: 10,
right:10
},
});
Just use that CSS in your code:
.floating-btn {
position:fixed;
bottom:10;
right: 10;
}
That's it
I just want to know how to display a part of an image in react-native
class InstaClone extends Component {
render() {
return(
<View style={{ flex:1, width:100 + "%", height:100 + "%" }}>
<View style={styles.TopNavStyle}>
<Text style={styles.TopNavTextStyle}>
Instagram
</Text>
</View>
<View style = {styles.userBar}>
<View style = {{ flexDirection:"row" , alignItems:"center" }}>
<Image style = {styles.userPic}
source = {require('../assets/images/photoUser.jpg')}/>
<Text style = {{marginLeft : 10}}>
Mohcouch
</Text>
</View>
<View>
<View style={styles.imageContainer}>
<ImageBackground
source={require('../assets/images/icons.jpg')}
style={styles.image}
></ImageBackground>
</View>
</View>
</View>
<Image
style={{ width:"100%", height:400 }}
source={require('../assets/images/baby.jpg')}
/>
</View>
)
}
}
} , image: {
height: 500,
width: 500,
resizeMode:"cover",
translateX:-80,
translateY: -135,
},
imageContainer: {
height: 40,
backgroundColor:'transparent',
width: 40,
},
})
export default InstaClone
Result :
https://scontent.xx.fbcdn.net/v/t1.15752-0/p280x280/58461695_2231014486986604_1022634010985103360_n.png?_nc_cat=102&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=cb81348aae1c524ba9e93d1e02afb120&oe=5D44DF9C
the image of icons :
https://scontent.xx.fbcdn.net/v/t1.15752-0/p280x280/51865955_790216374668323_1242853141917990912_n.jpg?_nc_cat=102&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=1c6d18d17e24ec68771be2590fc934c4&oe=5D2CC871
React Native doesn's support a property background-position. It is better to separate the icons in different image files.
But there is a tricky workaround that you can use with your sprite image:
First, you need to round the image pixels in layout size (dp):
const width = PixelRatio.roundToNearestPixel(280);
const height = PixelRatio.roundToNearestPixel(280);
Note: 280 is the size of the image from your post
Then, we need to calculate the same proportion for the size of the icon:
const iconWidth = PixelRatio.roundToNearestPixel(30);
const iconHeight = PixelRatio.roundToNearestPixel(30);
Note: 30 is a random size for our icon, it can be any size.
Now, for the loading of the image, we will use the ImageBackground component from react-native. For imageStyle we will pass the positioning properties of the image:
imageStyle={{
resizeMode: 'cover',
width: width, height: height,
top: -15,
left: -15
}}
Your component should look like this:
<ImageBackground
source={{ uri: image_url }}
style={{ width: iconWidth, height: iconHeight, overflow: 'hidden' }}
imageStyle={{
resizeMode: 'cover',
width: width, height: height,
top: -15,
left: -15
}}
/>
Here is a working demo.
I want to images to take full width of device screen. I tried multiple CSS code but it doesn't take full width of screen.
Code:
<View style={styles.container}>
<View>
<Image source={require('./Images/rectangle/rectangle.png')} style={styles.backgroundImage}>
</Image>
</View>
</View>
const styles = StyleSheet.create({
container: {
flex: 1
},
backgroundImage: {
height: 200,
flexDirection: 'column',
alignSelf: 'stretch'
}
});
Above css doesn't work and I am getting white space on right side of image see screenshot:
I have also added images with different suffixes so that it will work for different screens see screenshot below:
try this:
backgroundImage:{
width: Dimension.get('window').width ,
alignSelf: 'center',
height: 200,
}
I have a ScrollView that has a top section with one background colour and a bottom section with another different colour.
When a user scrolls past the content and the view bounces (elastic over-extend), how could I make it so the background is consistent with either the top or the bottom, depending on the scroll direction?
I wouldn't play with the contentInset and contentOffset of the ScrollView as if your content changes, it might change the position of your scrollview.
You can do something very simple by just adding a View at the very top of your ScrollView:
// const spacerHeight = 1000;
<ScrollView>
{Platform.OS === 'ios' && (
<View
style={{
backgroundColor: 'red',
height: spacerHeight,
position: 'absolute',
top: -spacerHeight,
left: 0,
right: 0,
}}
/>
)}
</ScrollView>
On iOS, you can render a spacer View on top of the ScrollView, and use contentInset to render it "off-screen", contentOffset to set the initial scroll position to offset the inset:
render() {
const isIos = Platform.OS === 'ios'
const SPACER_SIZE = 1000; //arbitrary size
const TOP_COLOR = 'white';
const BOTTOM_COLOR = 'papayawhip';
return (
<ScrollView
style={{backgroundColor: isIos ? BOTTOM_COLOR : TOP_COLOR }}
contentContainerStyle={{backgroundColor: TOP_COLOR}}
contentInset={{top: -SPACER_SIZE}}
contentOffset={{y: SPACER_SIZE}}>
{isIos && <View style={{height: SPACER_SIZE}} />}
//...your content here
</ScrollView>
);
}
Because contentInset and contentOffset are iOS only, this example is conditioned to degrade gracefully on Android.
The accepted solution did not work well for me because I need to put flexGrow: 1 on the contentContainerStyle. Using insets/offsets didn't make the content grow the way I want, otherwise it worked not so bad.
I have another solution to suggest: putting a bicolor background layer under a transparent ScrollView, and add colors to your scrollview content. This way, on ios bounce, the bicolor layer under the scrollview will reveal itself.
Here's what I mean by bicolor layer (here the scrollview is empty and transparent)
Now if I put back the ScrollView children (which if a body with blank background, and a footer with yellow background), I get this:
As long as you don't bounce more than 50% of the scrollview height, you will see the appropriate background color.
Here's a component you can use to wrap your scrollview.
const AppScrollViewIOSBounceColorsWrapper = ({
topBounceColor,
bottomBounceColor,
children,
...props
}) => {
return (
<View {...props} style={[{ position: 'relative' }, props.style]}>
{children}
<View
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: -1, // appear under the scrollview
}}
>
<View
style={{ flex: 1, backgroundColor: topBounceColor }}
/>
<View
style={{ flex: 1, backgroundColor: bottomBounceColor }}
/>
</View>
</View>
);
};
And here's how you use it:
<AppScrollViewIOSBounceColorsWrapper
style={{flex: 1}}
topBounceColor="white"
bottomBounceColor="yellowLancey"
>
<ScrollView style={{flex: 1}}>
<WhiteBackgroundBody/>
<YellowBackgroundFooter />
</AppScrollView>
</AppScrollViewIOSBounceColorsWrapper>
Make sure to NOT set a background color to the scrollview, otherwise the bicolor layer will never reveal itself (backgroundColor on contentContainerStyle is fine)
This is, I think the most stupid simple way i found to do it:
<ScrollView style={{backgroundColor: '#000000'}}>
[...]
<View style={{position: "absolute", bottom: -600, left: 0, right: 0, backgroundColor: '#FFFFFF', height: 600}}/>
</ScrollView>
You may adjust the height/bottom absolute value to your likings depending on how far you think the user could scroll.
I personally implemented that into a <ScrollBottom color={"white"}/> component for ease of use in all my ScrollViews
For me, the simplest solution is modification based on Sebastien Lorber answer which doesn't include wrapping, just calling it before (or after) ScrollView component:
Create component:
interface IScrollViewBackgroundLayer {
topBounceColor: string;
bottomBounceColor: string;
}
export const ScrollViewBackgroundLayer = ({
topBounceColor,
bottomBounceColor,
}: IScrollViewBackgroundLayer): ReactElement => (
<View
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: -1, // appear under the scrollview
}}>
<View style={{ flex: 1, backgroundColor: topBounceColor }} />
<View style={{ flex: 1, backgroundColor: bottomBounceColor }} />
</View>
);
and use it like this:
return (
<SafeAreaView style={styles.container}>
<ScrollViewBackgroundLayer topBounceColor={topBounceColor} bottomBounceColor={bottomBounceColor} />
<ScrollView>
...
</ScrollView>
</SafeAreaView>