How to handle with inconsistent react native header using navigationOptions? - ios

currently i'm working on react native app using component class. having problem when deal with header using navigationOptions. there are different behaviour on some iOS device model. Everything look good on iPhone X, when jump to iPhone 11 and above, it become worst.
Header will move upward until overlap status bar section and make it hard to touch
static navigationOptions = ({navigation, navigation: { state } }) => {
return {
title: '',
headerStyle: {
backgroundColor: '#EF6C00'
},
headerRight: (
<TouchableHighlight style={styles.headerBarIconTouch, {paddingRight: Layout.window.width * 0.05}} underlayColor='transparent'
onPress={() => { state.params.showLogin() }}>
<CustomText h5 color={'yellow'} >TW Logins</CustomText>
</TouchableHighlight>
),
headerLeft: (
<TouchableHighlight style={styles.headerBarIconTouch, {paddingLeft: Layout.window.width * 0.05}} underlayColor='transparent'
onPress={() => { state.params.showHelp() }}>
<View style={{flexDirection: 'row'}}>
<Image source={require('../assets/image/contact_support.png')}/>
<CustomText h5 color={'yellow'} translate> Contact Support</CustomText>
</View>
</TouchableHighlight>
)
}
};

Related

Keyboard covers one of the inputs in my React Native where view is centered using justifyContent

So I have this react native code where I centered multiple inputs inside view, when I click the lower located input my keyboard seems to overrun it so I cannot see what I'm typing on my iOS simulator (iPhone 14). The error seems to be on iOS only and Android is fine. note that I also have bottom Navigation bar.
https://ibb.co/sV0Xk9L
<SafeAreaView style={{ flex: 1}}>
{...}
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<View style={styles.profile_info}>
<KeyboardAvoidingView behavior={'position'} keyboardVerticalOffset={dimensions.height < 668 && phoneSelected ? 120 : 40}>
<ScrollView>
<View>
<Input />
<View/>
<View>
<Input />
<View/>
<View>
<Input />
<View/>
</ScrollView>
</KeyboardAvoidingView>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
{ // styling below }
function getProfileStyles(theme) {
return StyleSheet.create({
profile_info: {
flex: 1,
marginHorizontal: 20,
justifyContent: 'center',
},
});
}
I have tried using the solution found here https://stackoverflow.com/a/51169574/11288070 which uses #react-navigation/elements deps
import { useHeaderHeight } from '#react-navigation/elements'
something else that I do is changing from justifyContent to use topPadding: dimensions.height / 5 but it's not interactive.
Use:
KeyboardAwareScrollView from "react-native-keyboard-aware-scroll-view"
library instead of KeyboardAvoidingView

React Native Responsive View Iphone x

I face a little problem while styling for Iphone X, I used widthPercentageToDP and heightPercentageToDP from react-native-responsive-screen to make the view similar, in most of the devices it worked perfectly except Iphone X, the difference is a little bit but I want to make the view accurate as possible.
<View style={styles.container}>
<ImageBackground source={require('../../assets/acc.png')} style={styles.bgImg}>
<View style={styles.headerView}>
<FontAwesome style={styles.setting} name="cog" size={hp('4%')} color="#6B6466" />
<Text style={styles.headerText}>My Account</Text>
</View >
<View style={styles.imgView} >
<Image source={require('../../assets/user.png')} style={styles.accImg}/>
<Text style={styles.name}> John doe</Text>
<Text style={styles.number}> 123456789</Text>
</View>
<View style={styles.bottomView}>
<View style={styles.bottomView2}>
<TouchableOpacity style={styles.inboxView}>
<Text style={styles.inboxNumber}>12</Text>
<Text style={styles.inboxText}>Inbox</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.inboxView}>
<Text style={styles.inboxNumber}>17</Text>
<Text style={styles.inboxText}>Sent</Text>
</TouchableOpacity>
</View>
</View>
</ImageBackground>
</View>
Style
const styles = StyleSheet.create({
container: {
flex: 1, backgroundColor: '#fff'
},
bgImg:{
height: hp('40%'), width: '100%'
},
setting:{
color:'white'
},
headerText:{
flex:1,textAlign:'center',fontSize:wp('5%'),fontFamily:Fonts.Cairo,marginRight:10,color:'white'
},
headerView:{
flexDirection:'row',justifyContent:'space-between',marginHorizontal:10,marginTop:hp('5%')
},
imgView:{
flex:1,
alignItems:'center'
},
accImg:{
height:wp('30%'),width:wp('30%'),borderRadius:wp('15%'),marginTop:wp('3%')
},
name:{
color:'white',fontFamily:Fonts.Cairo,fontSize:wp('4%'),textAlign:'center'
},
number:{
color:'white',fontSize:wp('4%'),textAlign:'center'
},
bottomView:{
flex:1,
justifyContent:'flex-end'
},
inboxNumber:{
color:'white',
textAlign:'center',
fontSize:wp('4%')
},
inboxText:{
color:'white',
textAlign:'center',
fontSize:wp('4%'),
fontFamily:Fonts.Cairo
},
bottomView2:{
flexDirection:'row',
justifyContent:'space-between',
marginHorizontal:10,
marginBottom:wp('3%')
},
});
Output
The Iphone X is rendering the real result, this is how your layout looks like, the other devices don't have enought space for rendering and they "overlap" the views ... Look at your views, you have an imagebackground wich takes 40% of the screen , inside you have a header wich will render normally since it's higher in the render function.
Below you have a view with flex :1 and inside an image with screen based values with 2 rows of text below , if the image is lager than the view it will push the text outside of the view , invading the below neighbour view
BELOW that you have a view with the 2 touchableopacitys . they should not be overlapping. In fact, try adding overflow:'hidden' to the imgView style
------header------
///////IMAGE////////
///////IMAGE////////
///////Johndo///////
///////12345////////
Button//////Button
Just like the iphone is showing
<ImageBackground source={require('../../assets/acc.png')} style={styles.bgImg}>
<View style={styles.headerView}>
//here is the header
</View >
<View style={styles.imgView} >
// below is the image , the name , and the number
</View>
<View style={styles.bottomView}>
//below it should be the 2 buttons , not overlapping, BELOW
</View>
</ImageBackground>

How to use KeyboardAvoidingView with FlatList?

I have a FlatList component with an Input inside each row. When I select the input I want it to scroll up above the keyboard.
My code:
return (
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }} >
<FlatList
style={{ flex: 1, backgroundColor: '#fff' }}
data={ds}
renderItem={({ item }) => <ListItem data={item} />}
ListFooterComponent={this.renderButton}
/>
</KeyboardAvoidingView>
);
In this scenario, the FlatList is never loaded. When I delete flex:1 from both components, FlatList renders properly but selecting an Input does not make it scroll up
You can trying using react-native-keyboard-aware-scroll-view
https://github.com/APSL/react-native-keyboard-aware-scroll-view
It comes with KeyboardAware[ScrollView, ListView, SectionView, FlatList] which accepts the same props as their corresponding components from RN. I have used that and it worked for me.
render() {
return (
<KeyboardAwareFlatList
style={{flex: 1}}
data={this.state.data}
renderItem={({item}) => (
<View style={{flex: 1}}>
<Image
source={item.v}
style={{height:200, width: 200}}
/>
<TextInput
placeholder="enter text1"
/>
</View>
)}
/>
);
}
You could try using the library react-native-keyboard-spacer as an alternative to KeyboardAvoidingView.
Install:
npm install --save react-native-keyboard-spacer
Use it like this:
import KeyboardSpacer from 'react-native-keyboard-spacer'
...
<View style={{flex: 1}}>
<FlatList
style={{flex: 1}}
data={ds}
renderItem={({ item }) => <ListItem data={item} />}
/>
{/* The view that will expand to match the keyboard height */}
<KeyboardSpacer />
</View>
Try this:
<KeyboardAvoidingView behavior='position' keyboardVerticalOffset={xyz} >
You can remove the property 'keyboardVerticalOffset' or play with the value of xyz,
just find out the better value which fits in your case.
For anyone on a similar path as mine. I was not able to use KeyboardAvoidingView because it depends on ScrollView which conflicts with Flatlist. I couldn't used the header and footer option in Flatlist as I'm using it as a generated thing in a search selection box so it has to be contained.
For me there is a difference in how Android and iOS calculate absolute position. Android considers the bottom to be the top of the keyboard and iOS it is the bottom of the screen when the keyboard is showing.
It turns out to be not that difficult to just put a View around the content you want to remain above the keyboard and just dynamically set the height of it on iOS. This isn't even really necessary on Android as it follows the keyboard if the View is position: absolute and bottom: 0.
This heavily borrows from here: https://stackoverflow.com/a/60682069/438322
Thanks to Kevin Amiranoff
Here's a basic example using hooks.
function YourComponent(props){
const onKeyboardWillShow = e => {
setKeyboardHeight(e.endCoordinates.height);
};
const onKeyboardWillHide = () => {
setKeyboardHeight(0);
};
useEffect(() => {
// These listeners on ios are a little more snappy but not available on Android
// If you want to use this on Android use keyboardDidShow/Hide
if (Platform.OS === 'ios') {
Keyboard.addListener('keyboardWillShow', onKeyboardWillShow);
Keyboard.addListener('keyboardWillHide', onKeyboardWillHide);
}
return () => {
if (Platform.OS === 'ios') {
Keyboard.removeListener('keyboardWillShow', onKeyboardWillShow);
Keyboard.removeListener('keyboardWillHide', onKeyboardWillHide);
}
};
}, []);
const buttonHeight = 50;
return(
<View>
<Content bla={'bla'}/>
<View style={{
height: Platform.OS === 'ios'
? keyboardHeight + buttonHeight : buttonHeight,
position: 'absolute',
bottom: 0
}}>
{/* Keep this button above the keyboard */}
<Button style={{ height: buttonHeight }}/>
</View
</View>
)
}
this is my solution.
inverted={true} is the key
const dummy = [1,2,3,4,5,6,7,8,9,10]
<KeyboardAvoidingView >
<FlatList
data={dummy.reverse()}
inverted={true}
/>
</KeyboardAvoidingView>

How did I use the push() function in NavigatorIOS?

I want to Push to a new Component by the function push() in NavigatorIOS. It's like following:
renderRow(rowData, sectionID, rowID) {
var imgSource = IMAGE_URLS[rowID];
return (
<TouchableHighlight onPress = {() => {
this.props.navigator.push({
title: 'test',
component: example,
});
}}>
<View>
<View style={styles.row}>
<Image
source={imgSource}
style={styles.thum}
/>
<Text style={styles.text}>
{rowData}
</Text>
</View>
</View>
</TouchableHighlight>
);
}
But it will get a error when I click the TouchableHighlight.
I refered these two questions(1 and 2) before this. And the complete code is in this link
this is not binded to the class inside of renderRow().
You have to bind this either in the constructor:
this.renderRow = this.renderRow.bind(this);
or inside the render method:
render() {
var navStatusBarConfig = {
style: 'light-content',
}
return (
<View style={{ flex: 1, backgroundColor: '#F5FCFF'}}>
<View styles={styles.nav}></View>
<ListView
automaticallyAdjustContentInsets={false}
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
pageSize={4}
renderRow={this.renderRow.bind(this)}
/>
</View>
);
}
}
As to why, here is the reason :
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding
And a more complete blog about how to bind this [there are many, blogs and ways to bind this]) :
http://blog.andrewray.me/react-es6-autobinding-and-createclass/

React Native with Redux- Problems updating state correctly

I'm trying to upload images that I've base64 encoded but it doesn't seem to save the state to the app so I can upload the images to my server. The state only seems to save within the function and not to the entire component. Any suggestions as to what I'm doing wrong?
_selectImage(uri) {
NativeModules.ReadImageData.readImage(uri, (image) => {
this.state = {
selected: image
};
console.log(this.state)
});
}
render() {
return (
<View style={styles.container}>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }}>
<TouchableOpacity style={ styles.button } onPress={ this._addImage.bind( this ) }>
<Text>Add Image</Text>
</TouchableOpacity>
<TouchableOpacity style={ styles.button } onPress={this._uploadImage} >
<Text>Upload Image</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.container}>
<View style={styles.imageGrid}>
{ this.state.images.map((image) => {
return (
<TouchableHighlight onPress={this._selectImage.bind(null, image.uri)}>
<Image key={ _generateUUID() } style={styles.image} source={{ uri: image.uri }} />
</TouchableHighlight>
);
})
}
</View>
</ScrollView>
</View>
);
}
};
First, this-bind your _selectImage-method in constructor. Then use "setState" instead of this.state = {...}
You need only once to create the state object, best in constructor. Use then setState instead for further modifications.
constructor(props) {
super(props);
this.state = {selected: null};
this._selectImage = this._selectImage.bind(this);
}
_selectImage(uri) {
NativeModules.ReadImageData.readImage(uri, (image) => {
this.setState({
selected: image
});
console.log(this.state)
});
}

Resources