首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >KeyboardAvoidingView + React导航+安全区域视图=不工作

KeyboardAvoidingView + React导航+安全区域视图=不工作
EN

Stack Overflow用户
提问于 2020-10-20 22:29:42
回答 2查看 9.9K关注 0票数 5

我遇到的问题是,在使用嵌套在选项卡导航器内的react-navigation堆栈导航器进行基本聊天UI外观时,键盘将聊天消息输入字段隐藏在底部。所以我试着用KeyboardAvoidingView把键盘拉到一个可见的位置,但是键盘没有显示出来。我尝试过一种解决方案,其中包括将headerHeight添加到keyboardVerticalOffset支柱中,但它似乎要少50 be。例如,如果我将headerHeight + 50添加到keyboardVerticalOffset中,一切看起来都很棒,但是如果我将设备切换到iPhone 5或类似的东西,有了更小的屏幕、不同的SafeArea接口等等,键盘就会再次出现错误的位置。

我不知道罪魁祸首到底是什么,但我现在认为是顶部和/或底部的SafeArea填充物,我学到了这是“嵌入”。我正在尝试使用useSafeAreaInsets,但是所有的值都返回0!我希望使用这些内嵌来添加到keyboardVerticalOffset支柱中,这样避免视图就能正常工作。

我现在喜欢标签栏的样式,所以我想保持它的高度,填充和字体大小,但也许我做错了反应本机导航?也许我不能让这个选项卡条和堆栈导航器的样式像我想要的那样反应本地的?无论如何,我认为嵌入应该返回一个值,所以我认为这是一个问题所在。

注意,如果我移动<SafeAreaView>块来包围<TouchableWithoutFeedback>,而不是围绕<NavigationContainer>块,并删除添加到keyboardVerticalOffset中的50个额外像素,那么键盘就会正确地将输入字段推上,但是iPhone 11上的选项卡条图标会被压缩。在我写这篇文章时,我注意到这个更改现在有返回值的底部/topPadding变量吗?如果然后删除tabBarOptions,我会得到一个基本的选项卡外观,但我更喜欢初始选项卡的设计。

如何维护选项卡栏的当前样式,并让键盘避免在每个设备上输入聊天字段?

(注意:下面的useEffect用法是我尝试使用这个问题中概述的解决方案:https://github.com/th3rdwave/react-native-safe-area-context/issues/54)

App.js:

代码语言:javascript
运行
复制
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>
    );
  }
}

截图:

package.json:

代码语言:javascript
运行
复制
{
  "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"
  }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-10-29 20:47:17

因此,正如亚历克斯在这个Github问题上指出的那样,在SafeAreaProvider中包装应用程序修复了不能正确返回值的嵌入。

Tab条仍然被SafeAreaView包装的NavigationContainer压紧,我通过向Tab.NavigatortabStyle支柱增加70的高度来修正这个问题。然后又出现了另一个问题,其中TicketScreen的堆栈头太大了,并且通过将headerStatusBarHeight: 0添加到Stack.Navigator screenOptions支柱中从这个Stack.Navigator中修复。这些嵌入式只是在useEffect内部正确返回,使用了setState,然后我使用topPadding值将其添加到keyboardVerticalOffset道具中,键盘弹出而没有隐藏在iPhone 11和iPhone 6s上的输入字段!

更新注意:确保还将keyboardHidesTabBar: (Platform.OS == "ios" ? false : true)设置为Tab.Navigator的支柱。

以下是完整的工作代码:

代码语言:javascript
运行
复制
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>
    );
  }
}
票数 5
EN

Stack Overflow用户

发布于 2020-10-29 20:37:51

就像我在相应的GitHub问题中发布的一样,用<SafeAreaProvider>包装应用程序可以解决以下问题:

代码语言:javascript
运行
复制
export default class App extends Component {
  render(){
    return (
      <SafeAreaProvider>
        ...
      </SafeAreaProvider>
    );
  }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64454207

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档