如何使用 TabNavigation 在反应导航中显示 headers

how to display headers in react navigation with TabNavigation

我注意到 StackNavigation 中的视图显示 header 标题,但如果我在 TabNavigation 中设置相同的屏幕,它不会显示 header。如果我将 StackNavigation 包裹在每个选项卡周围,或者将嵌套在 StackNavigation 中的 TabNavigation 包裹起来,它只会显示 header。

为什么 TabNavigation 中的屏幕不显示 header - 这是预期的行为吗?如果是这样,是在每个选项卡中都有一个 StackNavigation 还是在 TabNavigation 周围有一个大的 StackNavigation 更好?

// 标签导航不在每个屏幕中显示 header 标题

const TabsNavigator = TabNavigator({
  Home: {
    screen:HomeScreen,
  },
  Profile: {
    screen: ProfileScreen,
  },
}, {
  tabBarOptions: {
    activeTintColor: '#e91e63',
  },
  navigationOptions: {
    header: {
      visible: true,
    },
  },
});

Header 当我将其包装在 StackNavigator

中时显示
default StackNavigator({
  Home: { screen: TabsNavigator },
});

还是这样比较好

 export TabsNavigator = TabNavigator({
      Home: {
        screen:StackNavigator({
          Home: { screen: HomeScreen },
        }),
      },
      Profile: {
        screen: StackNavigator({Profile: {screen: ProfileScreen}}),
      },
    }, {
      tabBarOptions: {
        activeTintColor: '#e91e63',
      },
      navigationOptions: {
        header: {
          visible: true,
        },
      },
    });

根据 React-Navigation TabNavigator Docs 没有 header 导航选项。因此,当您编写以下代码时,您实际上是在设置一个不存在的值,因此您所做的不会影响任何事情。

navigationOptions: {
  header: { visible: true },
}

遗憾的是,在这种情况下您需要 StackNavigator。

即使这是一个相当古老的问题,几天前我自己也遇到过这个问题,所以我会加上我的两分钱,希望这对将来的其他人也有用。

React Navigation 是一款令人惊叹的产品,具有大量的自定义功能,但有时一开始也会让人感到困惑,这也适用于文档的某些部分。 navigationOptions 在当前版本状态下,对所有屏幕都很常见,但 "list of available navigation options depends on the navigator the screen is added to." https://reactnavigation.org/docs/tab-navigator.html#navigationoptions-used-by-tabnavigator 因此 header 选项不起作用,因为它不适用于 TabNavigator本身。

关于哪种方法最好的问题取决于您希望通过应用程序本身的导航来完成什么。如果将 TabNavigator 放在 StackNavigator 中,header 组件将对 TabNavigator 中的所有选项卡通用,这意味着选项卡转换将生效,但 header 不会从它的顶部位置移动。另一方面,如果您选择在每个选项卡内嵌套 StackNavigator,则 header 将呈现在每个选项卡内,这意味着 header 将沿着选项卡过渡动画移动。

我做了一个快速的demo for you to see the difference, the code如果你想玩的话也可以

我使用的一般结构是创建一个选项卡导航器,然后为每个选项卡呈现堆栈。在此示例中,我不希望在我的 LiveMap 上使用 header,但我希望在其他两个选项卡上使用。命名由您决定,只要有意义。然后在每个堆栈中渲染有意义的屏幕。

const Tab = createBottomTabNavigator();
const TrainStack = createStackNavigator();
const MapStack = createStackNavigator();
const BusStack = createStackNavigator();

然后每个堆栈呈现我想要的那个子集的任何屏幕

const MapNavigator = () => {
  return (
    <MapStack.Navigator
      initialRouteName="LiveMap"
      screenOptions={{headerShown: false}}>
      <MapStack.Screen name="Live Map" component={LiveMap} />
      <MapStack.Screen name="Show Routes" component={AllRoutes} />
      <MapStack.Screen name="Select Routes" component={SelectRoutes} />
    </MapStack.Navigator>
  );
};

选项卡导航器将为每个选项卡呈现我的堆栈。

const MainNavigator = () => (
  <Tab.Navigator
    tabBarOptions={{
      activeTintColor: 'orange',
      keyboardHidesTabBar: true,
    }}
    initialRouteName="Live Map">
    <Tab.Screen
      name="Buses"
      component={BusNavigator}
      options={{
        tabBarLabel: 'Buses',
        tabBarIcon: ({color, size}) => (
          <BusIcon height={30} width={30} color={color} />
        ),
      }}
    />
    <Tab.Screen
      name="Live Map"
      component={MapNavigator}
      options={{
        tabBarLabel: 'Map',
        tabBarIcon: ({color}) => (
          <View
            height={100}
            width={100}
            style={{
              display: 'flex',
              alignItems: 'center',
              backgroundColor: '#fff',
              paddingTop: 8,
              borderRadius: 70,
            }}>
            <MapIcon height={50} width={50} color="orange" />
          </View>
        ),
      }}
    />
    <Tab.Screen
      name="Trains"
      component={TrainNavigator}
      options={{
        tabBarLabel: 'Trains',
        tabBarIcon: ({color, size}) => (
          <TrainIcon height={30} width={30} color={color} />
        ),
      }}
    />
  </Tab.Navigator>

我知道这个问题已经有了答案,但我只想展示一下我能够使用 React Navigation > 5.x.x.

为自己解决这个问题的方法

Juan 的回答描述了设置它的最佳方式,同时也展示了它是如何完成的,以便人们可以理解导航的外观,这对我们视觉学习者也有一点帮助。这对我来说当时有点难以理解,但希望以后能对其他人有所帮助。

我发现查看此问题的最佳方法是分解应用程序路由的层次结构。对我来说,我的看起来像这样。

               LoginNavigation                      HomeNavigation   
                     |                                    |
      LoginScreen        RegisterScreen        HomeStack      ProfileStack
                                                   |               |
                                              HomesScreen     ProfileScreen

我的 LoginNavigation 堆栈包含两个屏幕:登录屏幕和注册屏幕。在我的应用程序中,我不需要在此处设置选项卡导航,因此将这些屏幕包裹在 StackNavigator 中是合适的。另一方面,我有我的 HomeNavigation 堆栈,其中包含一个主屏幕和一个配置文件屏幕。唯一的问题是我的屏幕被包裹在 TabNavigator 中,它不会产生 header.

为了解决这个问题,我们实际上需要在 StackNavigators 中包装我们的两个屏幕(HomeScreen 和 ProfileScreen),然后用 TabNavigator 包装所有内容。意识到这一点后,这样做实际上更有意义。为什么?好吧,假设在我的主屏幕上有一些 post,我希望用户能够看到 post 以及它附带的所有评论。好吧,我不会为此设置另一个选项卡,因为那会很愚蠢。相反,我们会像这样将它添加到我们的 HomeStack 导航中。

                       HomeNavigation   
                             |
            HomeStack                 ProfileStack
                 |                         |
     HomesScreen    PostScreen       ProfileScreen

当查看像 ProfileStack 这样的东西时,这个结构现在看起来很明显,这个堆栈可能有多个分支,例如设置、关注者、图片...... 希望显示结构可以帮助别人理解这一点,也对我有所帮助。

下面是我的 AppStackNavigator 文件,用于查看代码中的结构。

import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import Login from '../screens/Login';
import Register from '../screens/Register';
import FirstProfileImage from '../screens/FirstProfileImage';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Home from '../screens/Home';
import Profile from './../screens/Profile';

const LoginStack = createStackNavigator();
const HomeTabs = createBottomTabNavigator();
const HomeStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const AppStack = createStackNavigator();

const LoginStackNavigator = () => (
  <LoginStack.Navigator screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}}>
    <LoginStack.Screen name="Login" component={Login}/>
    <LoginStack.Screen name="Sign Up" component={Register}/>
    <LoginStack.Screen name="Profile Image" component={FirstProfileImage}/>
  </LoginStack.Navigator>
)

const HomeStackNavigator = () => (
  <HomeStack.Navigator>
    <HomeStack.Screen name="Home" component={Home} />
  </HomeStack.Navigator>
)

const ProfileStackNavigator = () => (
  <ProfileStack.Navigator>
    <ProfileStack.Screen name="Profile" component={Profile} />
  </ProfileStack.Navigator>
)

const HomeTabNavigator = () => (
    <HomeTabs.Navigator>
      <HomeTabs.Screen name="HomeStack" component={HomeStackNavigator} />
      <HomeTabs.Screen name="ProfileStack" component={ProfileStackNavigator} />
    </HomeTabs.Navigator> 
)

const AppStackNavigator = () => (
    <AppStack.Navigator initialRouteName={'HomeScreen'} screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}} headerMode='none'>
      <AppStack.Screen name="LoginScreen" component={LoginStackNavigator}/>
      <AppStack.Screen name="HomeScreen" component={HomeTabNavigator}/>
    </AppStack.Navigator>  
)

export default AppStackNavigator;

我同意这有点矫枉过正,我们可以消除必须跟踪每个导航器的问题,但在他们为此添加功能之前,这是目前有效的方法。希望这个解释能有所帮助。感谢你来参加我的 TED 演讲。