【问题标题】:Nested TabNavigator inside StackNavigator: controlling the headerStackNavigator 内的嵌套 TabNavigator:控制标题
【发布时间】:2018-12-04 04:25:18
【问题描述】:

我的设置很像这样:

let Tabs = createBottomTabNavigator({
    screen1: Screen1,
    screen2: Screen2
})

let Stack = createStackNavigator({
    tabs: Tabs
    otherScreen: OtherScreen
})

堆栈导航器有一个标题,这很好。我想要的是根据我当前所在的选项卡获得不同的标题图标。

我正在使用以下版本:

"react": "16.3.1",
"react-native": "~0.55.2",
"react-navigation": "^2.2.5"

我考虑过切换我的设置,以便每个标签屏幕都有自己的StackNavigator,但我喜欢在切换标签时有滑动动画,我不希望标题图标滑动。标题栏应保持静态,但仅根据当前选项卡显示不同的图标。

【问题讨论】:

    标签: reactjs react-native react-navigation


    【解决方案1】:

    你可以像下面这样使用,https://reactnavigation.org/docs/en/stack-navigator.html

    //Screen1 Stack.
    
    const Screen1 = createStackNavigator ({
        Home: {
            screen: Home,
            navigationOptions: {
                header: null //Need to set header as null.
            }
        }
    });
    
    //Screen2 Stack
    
    const Screen2 = createStackNavigator ({
        Profile: {
            screen: Profile,
            navigationOptions: {
                header: null  //Need to set header as null.
            }
        }
    });
    
    
    let Tabs = createMaterialTopTabNavigator({
        Screen1:{
          screen: Screen1 //Calling Screen1 Stack.
        },
        Screen2:{
          screen: Screen2 //Calling Screen2 Stack.
        }
    },{ tabBarPosition: 'bottom' }) //this will set the TabBar at Bottom of your screen.
    
    let Stack = createStackNavigator({
        tabs:{
          screen: Tabs, //You can add the NavigationOption here with navigation as parameter using destructuring.
          navigationOptions: ({navigation})=>{
           //title: (navigation.state.routes[navigation.state.index])["routeName"]  
           //this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header. 
    
           //use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
    
            //title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
    
           //you can use switch case,on matching the route name you can set title of the header that you want and also header left and right icons similarly.
    
            switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
                case "Screen1":
                    return {
                        title: "Home", 
                        headerLeft: (<Button
                            onPress={()=> alert("hi")}
                            title="Back"
                            color="#841584"
                            accessibilityLabel="Learn more about this purple button"
                        /> ),
                        headerRight: <Button title= "Right"/>
                    }
                case "Screen2":
                    return { 
                        title: "Profile",
                        headerLeft: (<Button
                            onPress={()=> alert("hi")}
                            title="Back"
                            color="#841584"
                            accessibilityLabel="Learn more about this purple button"
                        /> ),
                        headerRight: <Button title= "Right"/>
                    }
                default:
                    return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
            }
          }
        },
        otherScreen:{
          screen: OtherScreen
        }
    })
    

    //导航选项

      navigationOptions: ({navigation})=>{
       //title: (navigation.state.routes[navigation.state.index])["routeName"]  
       //this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header. 
    
       //use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
    
        //title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
    
        switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
            case "Screen1":
                return {
                    title: "Home", 
                    headerLeft: (<Button
                        onPress={()=> alert("hi")} //Here you can able to set the back behaviour.
                        title="Back"
                        color="#841584"
                        accessibilityLabel="Learn more about this purple button"
                    /> ),
                    headerRight: <Button title= "Right"/>
                }
            case "Screen2":
                return { 
                    title: "Profile",
                    headerLeft: (<Button
                        onPress={()=> alert("hi")} 
                        title="Back"
                        color="#841584"
                        accessibilityLabel="Learn more about this purple button"
                    /> ),
                    headerRight: <Button title= "Right"/>
                }
            default:
                return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
        }
      }    
    

    //alert(navigation.state)

    {
    
        "routes":[
            {
                "key":"Screen1",
                "routeName":"Screen1",
                "routes":[
                    {
                        "key":"Home",
                        "routeName":"Home",
                    }
                ],
                "index":0,
                "isTransitioning":false,
                "key":"id-1530276062643-0"
            },
            {
                "key":"Screen2",
                "routeName":"Screen2",
                "routes":[
                    {
                        "key":"Profile",
                        "routeName":"Profile",
                    }
                ],
                "index":0,
                "isTransitioning":false,
                "key":"id-1530276062643-0"
            }
        ],
        "index":0,
        "isTransitioning":false,
        "routeName":"tabs",
        "key":"id-1530276062643-0"
    
    }
    

    //(navigation.state.routes[navigation.state.index])["routeName"] //(navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName

    this will give the current route name of the tab inside StackNavigation.
    

    上面的代码会将TabBar所在的根堆栈头中的标题设置为第一个路由,因此我们将TabBar中单个堆栈的标题设置为null。通过使用这种方式将在 TabBar 中切换屏幕时提供动画,因为标题将保持静态。

    你可以在这里找到工作副本https://www.dropbox.com/s/jca6ssn9zkzh9kn/Archive.zip?dl=0

    下载并执行以下操作。

    1. npm install //获取依赖项

    2. react-native upgrade //获取android和ios文件夹

    3. react-native link //链接依赖和库

    4. react-native run-ios(或)react-native run-android

      你可以使用上面的,如果有请告诉我。

    【讨论】:

    • @Anthony De Smet,我已经更新了我的答案,请检查一下,如果这能解决你的问题,请告诉我。
    • 虽然这可行,但您不能将标题标题更改为除了 routeName 之外的任何其他内容,对吗?例如“Screen2”标题将始终为“Screen2”,或者您可以以某种方式将其设置为:“子项 XXX 的屏幕标题”?
    • @Chris,我已根据您的要求更新了我的答案。您可以通过在与我们的 routeName 匹配的 navigationOptions 中使用 switch case 来设置标题、headerLeft、headerRight 等来实现它。通过这种方式,我们可以设置标题而与 routeName 无关。
    • @dhivyas 我在这里也有关于嵌套标签的问题stackoverflow.com/questions/55888800/… 你认为我们可以用类似的解决方案来解决吗?
    • 可以看看这个Q吗stackoverflow.com/questions/56999290/…@dhivyas
    【解决方案2】:

    您可以使用您当前的导航堆栈配置来实现所需的行为。您可能需要更改一些东西并组合几个属性,但是当您熟悉它时,这相当简单。

    我试着用一个小例子来解释。

    考虑拥有以下导航器;

    const Tabs = createBottomTabNavigator({
        screen1: Tab1,
        screen2: Tab2
    })
    
    const Stack = createStackNavigator({
        tabs: {
          screen: TabsPage,
          navigationOptions: ({navigation}) => {
            return { title: (navigation.state.params && navigation.state.params.title ? navigation.state.params.title : 'No Title' ) }
          }
        },
        otherScreen: Page
    })
    

    如您所见,我正在从导航状态设置标题参数。为了能够为该导航器设置参数,我们将从screenProps 属性中获得帮助;

    class TabsPage extends Component {
      onTabsChange = (title) => {
        this.props.navigation.setParams({ title })
      }
      render() {
        return(<Tabs screenProps={{ onTabsChange: this.onTabsChange }} />)
      }
    }
    

    我为标签导航器创建了一个包装器组件,并传递了一个设置标题参数的函数。

    对于最后一部分,我们需要知道如何以及何时使用我们传下来的函数。为此,我们将使用addListener 导航道具

    class Tab1 extends React.Component {
      setTitle = () => {
        this.props.screenProps.onTabsChange('Title from Tab 1')
      }
      componentDidMount() {
        this.props.navigation.addListener('willFocus', this.setTitle)
      }
      render() {
        return <View><Text>{'Tab1'}</Text></View>
      }
    }
    

    当我们的选项卡聚焦时,传递的函数将运行,然后为该选项卡设置标题。您可以使用此过程为标题设置不同的按钮或图标。你可以找到工作零食here

    【讨论】:

    • 这确实是一个更好的解决方案,但是您如何导航到“otherScreen”? this.props.navigation.navigate('Page');在 Tab1 或 Tab2 中不起作用
    • 你能看看这个Q吗stackoverflow.com/questions/56999290/…@bennygenel
    【解决方案3】:
    const RootStack = createStackNavigator(
      {
        Home: {screen: HomeScreen},
        FilterScreen: createMaterialTopTabNavigator({
            Tab1: {screen:Tab1Screen},
            Tab2: {screen: Tab2Screen},
        }),
      },
      {
        mode: 'modal',
        headerMode: 'none',
      }
    );
    
    
    render() {
        return <RootStack/>;
    }
    

    【讨论】:

    【解决方案4】:

    在 AppNavigation.js //或您定义路线的地方。

    let Tabs = createBottomTabNavigator({
        screen1: Screen1,
        screen2: Screen2
    })
    
    let Stack = createStackNavigator({
        tabs: Tabs
        otherScreen: OtherScreen
    },{
        headerMode:"float", //Render a single header that stays at the top and animates as screens are changed. This is a common pattern on iOS.
        headerTransitionPreset:"fade-in-place" //this will give a slight transition when header icon change.
      }
    )

    在 Screen1.js 中

    class Screen1 extends Component {
     
     static navigationOptions = ({ navigation }) => {
    
        return {
          ...
          headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen1"}/>,
          ...
        }
      }
      ...
    }

    注意:您可以添加标题、headersStyle、headerRight 以同样的方式阅读此链接header configuration 了解更多详情

    在 Screen2.js 中与 Screen1 类似

    class Screen2 extends Component {
     
     static navigationOptions = ({ navigation }) => {
    
        return {
          ...
          headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen2"}/>,
          ...
        }
      }
      ...
    }

    Header.js

    export const HeaderLeftImage = (props) => (
        <View style={{
           'add styles'
        }}>
            <TouchableOpacity onPress={() => {"Add action here" }}>
                <Image source={props.image} resizeMethod='resize' style={{ height: 30, width: 90, resizeMode: 'contain' }} />
            </TouchableOpacity>
        </View>
    )

    希望对您有所帮助,如果您对代码有任何疑问,请随时提问。

    【讨论】:

    【解决方案5】:

    如果你使用 react navigation

    const Tabs = TabNavigator({
        Tab1:{
             screen: Tab1,
             navigationOptions: ({navigation}) => {
                return { title: "Tab 1 Heading", tabBarLabel:"Tab 1 "}
        },
        }
        Tab2:{
           screen: Tab2
           navigationOptions: ({navigation}) => {
             return { title: "Tab 2 Heading", tabBarLabel:"Tab 2 "}
        }
        }
    })
    
    const Stack = StackNavigator({
        tabs: {
          screen: Tabs
          navigationOptions: ({navigation}) => {
            return { title: "Stack"}
          }
        },
        otherScreen: Page
    })
    

    我不确定他们为什么要删除此功能,当您尝试相同的功能时,它将无法在最新的 react-navigation 上运行。现在标题对象键似乎用于后备。

    这可能对某些用户有所帮助。如果您愿意,也可以尝试降级。

    我为我的项目升级了 React Navigation,我认为这种方式对某人有用

    const Tabs = TabNavigator({
         Tab1:{
            screen: Tab1,
            navigationOptions: ({navigation}) => {
               return { tabBarLabel:"Tab 1 "}
            }},
         Tab2:{
            screen: Tab2
            navigationOptions: ({navigation}) => {
               return { tabBarLabel:"Tab 2 "}
            }}
    });
    Tabs.navigationOptions = ({navigation})=>{
        const { routeName } = navigation.state.routes[navigation.state.index]; //This gives current route
        switch(routeName){
             case "Tab1":
               headerTitle="Tab 1";
               break;
             case "Tab1":
               headerTitle="Tab 1";
               break;
        }
    
     return {
       headerTitle: headerTitle
    }
    }
    

    【讨论】:

    【解决方案6】:

    您可以简单地使用属性headerShown: false。我创建了一个react-navigation 版本的工作示例:5.x.x,嵌套不同的导航器:stackdrawerbottom-tabs

    这里是 github 的链接: https://github.com/mvpbuddy/react-native-nested-navigators/

    如需更详细的概述,您可以查看我的博客: https://mvpbuddy.io/blog/detail/how-to-build-an-app-with-nested-stack-drawer-bottom-tab-navigators

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-27
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多