React native 键盘AvoidingView+;反应导航+;安全区域视图=不工作
我遇到的问题是,当使用嵌套在选项卡导航器中的React native 键盘AvoidingView+;反应导航+;安全区域视图=不工作,react-native,react-navigation,react-native-safe-area-view,React Native,React Navigation,React Native Safe Area View,我遇到的问题是,当使用嵌套在选项卡导航器中的react导航器堆栈导航器来显示基本的聊天UI外观时,键盘隐藏了底部的聊天信息输入字段。所以我试着keyboardavoidgview将键盘放到一个可见的位置,但键盘没有显示出来。我尝试了一个解决方案,包括将头锤添加到键盘垂直偏移道具,但它似乎关闭了约50px。例如,如果我将headerHeight+50添加到keyboardVerticalOffset中,一切看起来都很好,但是如果我将设备切换到iPhone 5或其他设备上,使用更小的屏幕、不同的安全
react导航器
堆栈导航器来显示基本的聊天UI外观时,键盘隐藏了底部的聊天信息输入字段。所以我试着keyboardavoidgview
将键盘放到一个可见的位置,但键盘没有显示出来。我尝试了一个解决方案,包括将头锤
添加到键盘垂直偏移
道具,但它似乎关闭了约50px。例如,如果我将headerHeight+50
添加到keyboardVerticalOffset
中,一切看起来都很好,但是如果我将设备切换到iPhone 5或其他设备上,使用更小的屏幕、不同的安全区域插入等,键盘将再次处于错误位置
我不确定罪魁祸首到底是什么,但我现在认为是顶部和/或底部的安全区域填充物,我学到的是“插图”。我正在尝试使用useSafetAreaInsets
,但所有值都返回0!我想使用这些插图添加到键盘垂直偏移量
道具中,以便避免视图正常工作
我现在喜欢选项卡栏的样式,所以我想增加它的高度、填充和字体大小,但也许我在使用react native navigation时做错了?也许我不能让这个标签栏和堆栈导航器按照我对React Native的要求进行样式化?无论如何,我相信插图应该返回一个值,所以我认为这就是问题所在
请注意,如果我将
块移动到
块的周围,而不是
块的周围,并移除添加到键盘垂直偏移量
的50个额外像素,则键盘会正确地向上推输入字段,但iPhone 11上的选项卡栏图标会被压扁。在我写这篇文章的时候,我注意到这个变化现在有底部/顶部添加返回值的变量?如果我随后删除选项卡选项
,我会得到工作选项卡的基本外观,但我更喜欢初始选项卡的设计
如何保持选项卡栏的当前样式,并使键盘避开每个设备上的聊天输入字段?
(注意:下面的useffect
用法是我尝试使用本期中概述的解决方案时使用的:)
App.js:
import React, { Component, useEffect, useState } from 'react';
import { View, KeyboardAvoidingView, TextInput, Text, Platform, TouchableWithoutFeedback, Keyboard, ActivityIndicator, SafeAreaView, ScrollView, Button, StatusBar } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator, useHeaderHeight } from '@react-navigation/stack';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { useSafeAreaInsets, useSafeAreaFrame } from 'react-native-safe-area-context';
import { Dimensions } from 'react-native';
const Stack = createStackNavigator();
function TicketStack() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: "dodgerblue",
elevation: 0, // remove shadow on Android
shadowOpacity: 0, // remove shadow on iOS
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "900",
fontSize: 26,
},
}}>
<Stack.Screen name="Ticket">
{(props) => <TicketScreen {...props} />}
</Stack.Screen>
<Stack.Screen name="Chat">
{(props) => <ChatScreen {...props} />}
</Stack.Screen>
</Stack.Navigator>
);
}
function HomeScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text><FontAwesome5 name={"home"} size={20} color={"dodgerblue"} /> Home screen!</Text>
</View>
);
}
function TicketScreen(props){
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Ticket screen :)!</Text>
<Button title="Go to Chat" onPress={() => props.navigation.navigate('Chat')} />
</View>
);
}
function CustomKeyboardAvoidingView({ children, style }) {
const headerHeight = useHeaderHeight();
console.log("headerHeight: " + headerHeight)
console.log("StatusBar.currentHeight: " + StatusBar.currentHeight)
const insets = useSafeAreaInsets();
console.log("insets.top: " + topPadding)
console.log("insets.bottom: " + bottomPadding)
const [bottomPadding, setBottomPadding] = useState(insets.bottom)
const [topPadding, setTopPadding] = useState(insets.top)
useEffect(() => {
setBottomPadding(insets.bottom)
setTopPadding(insets.top)
console.log("topPadding: " + topPadding)
console.log("bottomPadding: " + bottomPadding)
}, [insets.bottom, insets.top])
// const frame = useSafeAreaFrame();
// const windowHeight = Dimensions.get('window').height
// console.log("frame.height: " + frame.height)
// console.log("windowHeight: " + windowHeight)
// const safeAreaHeight = windowHeight - frame.height
// console.log("safeAreaHeight: " + safeAreaHeight)
// safeAreaHeight is too much, needs to just be bottom or top padding from safearea
return (
<KeyboardAvoidingView
style={style}
behavior={Platform.OS == "ios" ? "padding" : "height"}
keyboardVerticalOffset={headerHeight + 50}
>
{children}
</KeyboardAvoidingView>
);
}
function ChatScreen(){
return(
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<CustomKeyboardAvoidingView style={{backgroundColor: "#fff", flex: 1, flexDirection: "column", justifyContent: "space-between" }}>
<View style={{backgroundColor: "dodgerblue", paddingVertical: 15}}>
<View style={{ margin: 10, marginBottom: 15}}>
<ActivityIndicator size="large" style={{marginBottom: 10}}/>
<Text>Waiting for more info here....</Text>
</View>
</View>
<ScrollView style={{backgroundColor: "tomato", paddingVertical: 15}}>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
</ScrollView>
<View style={{backgroundColor: "yellow", paddingVertical: 15}}>
<TextInput placeholder="Type your message here..." />
</View>
</CustomKeyboardAvoidingView>
</TouchableWithoutFeedback>
)
}
const Tab = createBottomTabNavigator();
// TODO:
// - removing safeareaview makes tabs squished and icon nearly invisible, but chat message input fields avoids keyboard properly (squished can be fixed by removing tabBarOptions)
// - having safeareaview makes tabs look good, but chat message input field is hidden by keyboard
// - safeareainsets? why are they 0? i would be adding the bottom or top padding of insets to the vertical offset of the keyboard avoiding view.
export default class App extends Component {
render(){
return (
<SafeAreaView style={{flex: 1, backgroundColor: "dodgerblue"}}>
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = 'home';
} else if (route.name === 'Ticket') {
iconName = 'question';
}
return <FontAwesome5 name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
style: {
height: 70
},
activeTintColor: "#fff",
inactiveTintColor: "dodgerblue",
inactiveBackgroundColor: "#fff",
activeBackgroundColor: "dodgerblue",
tabStyle: {
paddingTop: 10,
paddingBottom: 10
},
labelStyle: {
fontSize: 14
},
}}>
<Tab.Screen name="Home">
{(props) => <HomeScreen {...props} />}
</Tab.Screen>
<Tab.Screen name="Ticket">
{(props) => <TicketStack {...props} />}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>
</SafeAreaView>
);
}
}
{
"name": "ReactNativeTest",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"@react-native-community/masked-view": "^0.1.10",
"@react-navigation/bottom-tabs": "^5.9.2",
"@react-navigation/native": "^5.7.6",
"@react-navigation/stack": "^5.9.3",
"react": "16.13.1",
"react-native": "0.63.3",
"react-native-gesture-handler": "^1.8.0",
"react-native-reanimated": "^1.13.1",
"react-native-safe-area-context": "^3.1.8",
"react-native-screens": "^2.11.0",
"react-native-vector-icons": "^7.1.0"
},
"devDependencies": {
"@babel/core": "7.11.6",
"@babel/runtime": "7.11.2",
"@react-native-community/eslint-config": "1.1.0",
"babel-jest": "25.5.1",
"eslint": "6.8.0",
"jest": "25.5.4",
"metro-react-native-babel-preset": "0.59.0",
"react-test-renderer": "16.13.1"
},
"jest": {
"preset": "react-native"
}
}
正如我在相应的文章中所说,在
中包装应用程序解决了这个问题:
导出默认类应用程序扩展组件{
render(){
返回(
...
);
}
}
正如Alex所指出的,将应用程序包装在SafeAreaProvider
中,修复了插入部分无法正确返回值的问题
选项卡栏仍然被SafeAreaView
压扁,包裹着NavigationContainer
,我通过在tabStyle
prop的Tab.Navigator
中添加70的高度来修复这个问题。然后又出现了另一个问题,TicketScreen的堆栈标题太大,因此通过将headerStatusBarHeight:0
添加到Stack.Navigator
screenOptions
prop来修复。插页只在useffect
内部正确返回,使用setState
用法,然后我使用topPadding
值添加到keyboardVerticalOffset
道具中,键盘弹出时没有隐藏iphone11和iphone6s上的输入字段
更新说明:确保还将键盘HIDESTABBAR:(Platform.OS==“ios”?false:true)
设置为选项卡.导航器上的道具
以下是完整的工作代码:
import React, { Component, useEffect, useState } from 'react';
import { View, KeyboardAvoidingView, TextInput, Text, Platform, TouchableWithoutFeedback, Keyboard, ActivityIndicator, SafeAreaView, ScrollView, Button, StatusBar } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator, useHeaderHeight } from '@react-navigation/stack';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { useSafeAreaInsets, SafeAreaProvider } from 'react-native-safe-area-context';
const Stack = createStackNavigator();
function TicketStack() {
return (
<Stack.Navigator
screenOptions={{
headerStatusBarHeight: 0, // Header had increased size with SafeArea for some reason (https://github.com/react-navigation/react-navigation/issues/5936)
headerStyle: {
backgroundColor: "dodgerblue",
elevation: 0, // remove shadow on Android
shadowOpacity: 0, // remove shadow on iOS
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "900",
fontSize: 26,
},
}}>
<Stack.Screen name="Ticket">
{(props) => <TicketScreen {...props} />}
</Stack.Screen>
<Stack.Screen name="Chat">
{(props) => <ChatScreen {...props} />}
</Stack.Screen>
</Stack.Navigator>
);
}
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center' }}>
<Text><FontAwesome5 name={"home"} size={20} color={"dodgerblue"} />Home screen</Text>
</View>
);
}
function TicketScreen(props){
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Ticket screen</Text>
<Button title="Go to Chat" onPress={() => props.navigation.navigate('Chat')} />
</View>
);
}
function CustomKeyboardAvoidingView({ children, style }) {
const headerHeight = useHeaderHeight();
console.log("headerHeight: " + headerHeight)
console.log("StatusBar.currentHeight: " + StatusBar.currentHeight)
const insets = useSafeAreaInsets();
const [bottomPadding, setBottomPadding] = useState(insets.bottom)
const [topPadding, setTopPadding] = useState(insets.top)
useEffect(() => {
// This useEffect is needed because insets are undefined at first for some reason
// https://github.com/th3rdwave/react-native-safe-area-context/issues/54
setBottomPadding(insets.bottom)
setTopPadding(insets.top)
console.log("topPadding: " + topPadding)
console.log("bottomPadding: " + bottomPadding)
}, [insets.bottom, insets.top])
return (
<KeyboardAvoidingView
style={style}
behavior={Platform.OS == "ios" ? "padding" : "height"}
keyboardVerticalOffset={headerHeight + topPadding + StatusBar.currentHeight}
>
{children}
</KeyboardAvoidingView>
);
}
function ChatScreen(){
return(
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<CustomKeyboardAvoidingView style={{backgroundColor: "#fff", flex: 1, flexDirection: "column", justifyContent: "space-between" }}>
<View style={{backgroundColor: "dodgerblue", paddingVertical: 15}}>
<View style={{ margin: 10, marginBottom: 15}}>
<ActivityIndicator size="large" style={{marginBottom: 10}}/>
<Text>Waiting for more info here....</Text>
</View>
</View>
<ScrollView style={{backgroundColor: "tomato", paddingVertical: 15}}>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
<Text>Chat messages</Text>
</ScrollView>
<View style={{backgroundColor: "yellow", paddingVertical: 15}}>
<TextInput placeholder="Type your message here..." />
</View>
</CustomKeyboardAvoidingView>
</TouchableWithoutFeedback>
)
}
const Tab = createBottomTabNavigator();
export default class App extends Component {
render(){
return (
<SafeAreaProvider>
<SafeAreaView style={{flex: 1, backgroundColor: "dodgerblue"}}>
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = 'home';
} else if (route.name === 'Ticket') {
iconName = 'question';
}
return <FontAwesome5 name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
style: {
height: 70
},
activeTintColor: "#fff",
inactiveTintColor: "dodgerblue",
inactiveBackgroundColor: "#fff",
activeBackgroundColor: "dodgerblue",
tabStyle: {
paddingTop: 10,
paddingBottom: 10,
height: 70
},
labelStyle: {
fontSize: 14
},
keyboardHidesTabBar: (Platform.OS == "ios" ? false : true)
}}>
<Tab.Screen name="Home">
{(props) => <HomeScreen {...props} />}
</Tab.Screen>
<Tab.Screen name="Ticket">
{(props) => <TicketStack {...props} />}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>
</SafeAreaView>
</SafeAreaProvider>
);
}
}
import React,{Component,useffect,useState}来自'React';
从“react native”导入{视图、键盘AVOIDGVIEW、文本输入、文本、平台、无反馈触摸屏、键盘、活动指示器、安全区域视图、滚动视图、按钮、状态栏};
从'@react-navigation/native'导入{NavigationContainer};
从“@react navigation/bottom tabs”导入{createBottomTabNavigator};
从'@react navigation/stack'导入{createStackNavigator,useHeaderHeight};
从“反应本机向量图标/FontAwesome5”导入FontAwesome5;
从“react native safe area context”导入{useSafeAreaInsets,SafeAreaProvider};
const Stack=createStackNavigator();
函数TicketStack(){
返回(
{(道具)=>}
{(道具)=>}
);
}
功能主屏幕(){
返回(
主屏幕
);
}
功能标签屏幕(道具){
返回(
检票屏幕
props.navigation.navigate('Chat')}/>
);
}
函数CustomKeyboardAvoidingView({children,style}){
const headerHeight=使用headerHeight();
控制台日志(“headerHeight:+headerHeight”)
console.log(“StatusBar.currentHeight:+StatusBar.currentHeight”)
常量插入=使用安全区域插入();
const[bottomPadding,setBottomPadding]=useState(insets.bottom)
const[topPadding,setTopPadding]=useState(insets.top)
useffect(()=>{
//之所以需要此useEffect,是因为由于某些原因,插入最初是未定义的
// https://github.com/th3rdwave/react-native-safe-area-context/issues/54
setBottomPadding(插图底部)
setTopPadding(插图顶部)
日志(“topPadding:+topPadding”)
log(“底部填充:+bottomPadding”)
},[insets.bottom,insets.top])
返回(
{儿童}
);
}
函数ChatScreen(){
返回(
正在此处等待更多信息。。。。
聊天信息
聊天信息
聊天信息
聊天信息
聊天信息
聊天信息
聊天信息
聊天信息