React Native - ScrollView with scrolling text input at bottom - ios

I'm working on a react native mobile application for iOS and trying to make an overlaying TextInput remain sticky at the bottom of a ScrollView. My first thought was to move it outside the ScrollView, and this works but does not allow for the overlay of the component over the scrolling content.
Here's a sample of my code:
basic idea:
<ScrollView style={styles.tabView}>
<View>
</View>
<TextInput style={styles.input} />
</ScrollView>
I can only get it to do one of the following: 1) remain at bottom with no overlay and 2) only display at the bottom of the entire scroll view.
Is there a best of both worlds?
Any help at all with this would be greatly appreciated.
Thank you.

You can wrap the ScrollView and the TextInput inside a container and then style the TextView to half transparent and position 'absolute' and stick it to the bottom of the container..
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'blue'}}>
<View style={{height: 400, width: 400}}>
<ScrollView>
<View style={{backgroundColor: 'green'}}>
// Your data here
</View>
</ScrollView>
<TextInput style={{backgroundColor: '#c4c4c4dd', position: 'absolute', bottom: 0, left: 0, right: 0}} />
</View>
</View>
You can see this example which I've prepared for you. Enjoy.

The best and easiest way is to use the following tag as your codes container:
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={{flex:1}}
>
...
</KeyboardAvoidingView>

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

KeyboardAvoidingViev not working on iOS with react navigation header and material bottom tabs?

You find a minimal repo here and a minimal snack available here. They both show the issue nicely with very minimal code.
I found lots of questions online about how to use KeyboardAvoidingViev with react navigation e.g.
The popular proposed solution is
keyboardVerticalOffset={headerHeight + 64}
That does not work completely for me. The 64 seems arbitrary and somehow on iOS the view shrinks after closing the keyboard.
Here is what I know so far:
It is definitely react navigation (headers and/or Material Bottom Tabs Navigator) and KeyboardAvoidingViev working together. If you remove the Material Bottom Tabs Navigator, it works.
Solutions including SafeAreaView did make it worse
Solutions with behavior={"position"} did make it worse
Solutions with +64 are not working
I found out that the solution of David Scholz works nicely, if I remove the Material Bottom Tabs Navigator.
Everything works fine on android (Samsung Galaxy 7S and emulator). From what I know, I would recommend to avoid KeyboardAvoidingViev with react navigation on android. So in fact, KeyboardAvoidingViev is also not working fully for android, but you don't need to use it.
Any help is appreciated. Thank you!
I create a wrapping component like this and pass the offset from the screen so that it gets the current context.
import React from 'react'
import { ViewStyle } from 'react-native'
import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
type Props = {
children: any
keyboardVerticalOffset?: number
contentContainerStyle?: ViewStyle
}
export default function KeyboardWrapper({
children,
keyboardVerticalOffset,
...rest
}: Props): JSX.Element {
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
keyboardVerticalOffset={keyboardVerticalOffset}
{...(Platform.OS === 'ios' ? { behavior: 'padding' } : {})}>
<ScrollView
bounces={false}
showsVerticalScrollIndicator={false}
{...rest}>
{children}
</ScrollView>
</KeyboardAvoidingView>
)
}
Usage in the screen
If you have any sticky elements like topbar or bottom bar you need to add the height so that the keyboard adds the offset correctly.
import { useHeaderHeight } from '#react-navigation/elements'
const height = useHeaderHeight()
<KeyboardWrapper
keyboardVerticalOffset={height}
contentContainerStyle={{ flex: 1 }}>
// ... your elements
</KeyboardWrapper>
This is a working demo https://snack.expo.dev/#raajnadar/fix-keyboardavoidingview-not-working
There is no need to add 64 to the headerHeight. Here is how I would solve this problem.
const flatListRef = createRef()
const [data, setData] = useState()
const headerHeight = useHeaderHeight();
<View style={{ flex: 1 }}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={headerHeight}
style={{ flex: 1 }}>
<FlatList
data={data}
ref={flatListRef}
onContentSizeChange={() =>
flatListRef.current.scrollToEnd({ animated: true })
}
onLayout={() => flatListRef.current.scrollToEnd({ animated: true })}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Text style={{ flex: 1, paddingVertical: 20 }}>{item.id}</Text>
)}
/>
<View style={{ flex: 1, marginBottom: 40 }}>
<TextInput
style={{
backgroundColor: '#2E2E2E',
width: '100%',
borderRadius: 18,
height: 36,
paddingLeft: 10,
paddingRight: 10,
color: '#FFFFFF',
}}
/>
</View>
</KeyboardAvoidingView>
</View>
Here is a quick and dirty snack which contains an header from a StackNavigator.

White Screen on top part of iOS react native

I get white screen at the top part of my iOS react native app. I have SafeAreaView as a container housing my code. On some screens the white space is just at the top, some others, just bottom; while some top and bottom. Below is an image:
Below is what my code looks like:
<SafeAreaView style={styles.container}>
<ImageBackground source={backgroundImg} style={styles.image}>
{/* arrow section */}
<TouchableOpacity
style={styles.firstLevelContainer}
onPress={() => navigation.goBack()}>
<Image source={backIcons} style={styles.backIconsStyle}></Image>
</TouchableOpacity>
{/* text Section */}
<View style={styles.secondLevelContainer}>
<CustomText
color={colors.white}
size={30}
capitalize={'none'}
fontType={fonts.nunitoBold}>
{CapitalizeFirstLetter('Create\nan account')}
</CustomText>
</View>
<View style={styles.thirdLevelContainer}>
<ScrollView
style={styles.scrollviewContainer}>
{Formikform()}
</ScrollView>
</View>
<View style={styles.fourthLevelContainer}>
<CustomText
color={colors.white}
size={12}
style={{marginLeft: 10}}
capitalize={'none'}
fontType={fonts.nunitoLight}>
or sign in with
</CustomText>
<View style={styles.displayRow}>
<Image
source={facebookLogo}
style={styles.socialLoginIconSize}></Image>
<Image
source={googleLogo}
style={styles.socialLoginIconSize}></Image>
</View>
</View>
</ImageBackground>
</SafeAreaView>
StyleSheet
container: {
flex: 1,
flexDirection: 'column',
top: 0,
},
formContainer: {
display: 'flex',
marginBottom:hp(20),
marginLeft:hp(5)
},
image: {
flex: 1,
resizeMode: 'cover',
},
That's exactly how SafeAreaView works. If you want your image to cover all of the screen I'd suggest the following UI Hierarchy:
<ImageBackground>
<SafeAreaView>
// your content goes here
</SafeAreaView>
</ImageBackground>
Checkout this snack for more details

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>

React Native WebView rendering unexpected border at bottom

This is a little tricky to describe. I'm using a react native WebView component to make a mini-browser. It's working ok, but there's a strange black border at the bottom of it which I didn't put in. It appears when scrolling to or beyond the bottom of the webpage.
You can see the thin black line above the "blah" text below. The reason for the blah text and the pink section at the bottom was to rule out a stray borderBottom* style on the WebView or its container.
Another thing to note is that the border briefly disappears when reloading the page, so it seems it's in the WebView's html. But I don't understand how or why, because a) it appears on all websites and b) it doesn't appear in other iOS browsers.
Am I doing something strange here?
Screenshot:
Elements and Styles:
<View style={styles.container as any}>
<View style={styles.header as any}>
{this.addressbarButtonProps().map(props => React.createElement(Button, { ...props, key: props.title }))}
<TextInput
ref={ADDRESSBAR_REF}
placeholder="type url"
style={{ minWidth: this.screenWidth - 50 * this.addressbarButtonProps().filter(b => !b.hidden).length - 10 }}
/>
</View>
<WebView
ref={WEBVIEW_REF}
source={{uri: this.state.uri}}
style={{ ...styles.web, width: this.screenWidth, paddingBottom: 0, padding: 0 }}
scalesPageToFit={true}
allowsInlineMediaPlayback={true}
onMessage={m => this.onMessage(m.nativeEvent.data)}
injectedJavaScript={js}
startInLoadingState={true}
/>
<Text style={{ borderTopWidth:20, borderTopColor: "red" }}>blah</Text>
</View>
const styles = {
container: {
paddingTop: 20,
flex: 1,
alignItems: "center",
justifyContent: "space-between",
backgroundColor: "#fafafa",
borderBottomColor: "pink",
borderBottomWidth: 100
},
header: {
flexDirection: "row",
alignItems: "flex-start",
justifyContent: "flex-start",
alignSelf: "stretch"
},
web: {
flex: 1,
borderBottomColor: "#fafafa",
backgroundColor: "#fafafa"
}
}
Set the backgroundColor of the WebView to transparent and the "border" won't be rendered.
<Webview style={{backgroundColor: 'transparent'}} .../>

Resources