背景

上一篇文章详细讲解了react-navigation的使用,这篇文章主要说一下Stack NavigationTab Navigation嵌套的问题。

在iOS原生开发中,一般是在TabController里嵌套NavigationController,也就是说底部导航控制器里放多个堆栈导航控制器,每个堆栈导航控制器控制有独立的堆栈和状态。

但如果在使用react-navigation进行这种嵌套方式,由于根控制器是底部的TabNavigation,每次跳转到子控制器时,底部的导航栏不会隐藏。官方文档说可以用属性更改的方法隐藏底部导航栏,但不推荐,会影响性能。因此本文主要讲解如何使用堆栈导航器中嵌套底部导航控制器来解决这个问题。

实现方式

首先将Main控制器放入Stack Navigation中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class App extends Component {

render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
key="Main"
name="Main"
component={Main}
options={({route}) => ({
headerTitle: route.name
})}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

}

然后实现Main控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const Main = ({navigation, route}) => {

return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {``
return (
<Image style = {{ width: 25, height: 25 }}
source = {this.iconImage(route, focused)}
/>);
},
})}
tabBarOptions={{
activeTintColor: '#32B7FF',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home" component={HomeScreen} options={{title: 'Home'}}/>
<Tab.Screen name="Settings" component={SettingsScreen} options={{title: 'Settings'}}/>
</Tab.Navigator>
);
}

function iconImage(route, focused) {
let image;
if (route.name === 'Home') {
image = focused
? require('../../images/homePage_sel.png')
: require('../../images/homePage_nor.png');
} else {
image = focused
? require('../../images/setting_sel.png')
: require('../../images/setting_nor.png');
}
return image;
}


上面HomeSetting页面的实现这里就不再赘述,实现之后运行会发一HomeSetting顶部导航栏的标题和样式都是相同的,也就是Main控制器的。这是由于Tab Navigation作为Stack Navigation的根控制器,顶部导航栏的标题都是按照根控制器的设置来显示的。要实现Tab Navigaiton的子控制器都有自己的独立顶部导航栏,需要使用ReactHook方法,在return前面加上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
React.useLayoutEffect(() => {
navigation.setOptions({
headerTitle: this.getHeaderTitle(route),
headerRight: this.getHeaderRight(route),
headerLeft: this.getHeaderLeft(route),
});
}, [navigation, route]);

getHeaderTitle(route) {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
switch (routeName) {
case 'Home':
return '首页';
case 'Settings':
return '系统设置';
}
}

这里使用了ReactuseLayoutEffect方法,这个方法会从DOM里读取布局并同步把新布局添加了布局更新计划中,等一下次重新绘制就会更新上去。在方法中通过route名称动态地改变了导航栏的标题和左右侧的按钮,当底部导航栏按钮被点击进行切换时,顶底的导航栏显示也会同步更新。

至此,Stack NavigationTab Navigation嵌套时保证跳转时隐藏底部导航栏的最佳实践就完成了。