やりたいこと
React Navigation で createBottomTabNavigatorを使って画面をタブ表示していて、かつタブ内に createStackNavigatorでスタックナビゲータを入れている時。
このスタックの最初のページ以外はタブバーを非表示にしたいということ、あると思います。
イメージでいうと、こんな感じです。
import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'
const TabAStack = createStackNavigator({
ScreenA1,
ScreenA2, //タブバー非表示にしたい
ScreenA3, //タブバー非表示にしたい
});
export default createBottomTabNavigator({
TabAStack,
TabB,
TabC
});
今回は、その方法を紹介します。
前提
React Navigationのバージョン
今回試したReact Navigationのバージョンは以下です。
"react-navigation": "^2.9.1"
実現方法
stackNavigatorのnavigationOptions
で tabBarVisible
を指定してあげるだけです。
「どの画面に対して」を指定する場合には、少なくとも以下の2通りがあります。
- スタックのindexで切り分ける場合
- 画面の名前で切り分ける場合
スタックのindexで切り分ける場合
navigation.state.index
でスタックの何番目を表示しているのか分かるので、スタックの一番最初(タブ内に最初に表示される画面)だけタブバーを表示したい場合は以下のようにします。
stackNavigator.navigationOptions = ({ navigation }) => ({
//スタックの一番最初(ScreenA1)だけタブバーを表示する
tabBarVisible: navigation.state.index === 0,
});
全体
import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'
const TabAStack = createStackNavigator({
ScreenA1,
ScreenA2, //タブバー非表示にしたい
ScreenA3, //タブバー非表示にしたい
});
TabAStack.navigationOptions = ({ navigation }) => ({
//スタックの一番最初だけタブバーを表示する
tabBarVisible: navigation.state.index === 0,
});
export default createBottomTabNavigator({
TabAStack,
TabB,
TabC
});
画面の名前で切り分ける場合
stackNavigator.navigationOptions = ({ navigation }) => ({
const { index } = navigation.state;
// または、 const index = navigation.state.index;
const { routeName } = navigation.state.routes[index];
// または、 const routeName = navigation.state.routes[index].routeName;
//ScreenA1でのみタブバーを表示する
tabBarVisible = routeName === ScreenA1
});
(余談)navigation.stateの中身
上記の navigation.state
ですが、
ScreenA1=>ScreenA2=>ScreenA3
と遷移した時の中身は以下のようになっています。
index
で現在表示している画面のindexが分かり、また routes
の配列にはスタックの履歴(ScreenA1, ScreenA2, ScreenA3)が入っています。
そのため、routes[index]
で、現在の画面の情報を取得できます。
{
"key":"TabAStack",
"isTransitioning":false,
"index":2, //スタックの何番目か
"routes":[
{
"routeName":"ScreenA1", //ScreenA1の画面名
"key":"id-1536728255349-2"
},
{
"params":{}, //遷移時にパラメータを指定するとその情報がここに入ります。ヘッダーのタイトルとか。
"routeName":"ScreenA2", //ScreenA2の画面名
"key":"id-1536728255349-9"
},
{
"routeName":"ScreenA3", //ScreenA3の画面名
"key":"id-1536728255349-10"
}
],
"routeName":"TabAStack"}
ちなみに、一個前の画面(ScreenA2)に戻るとこうなります。
{
"key":"TabAStack",
"isTransitioning":false,
"index":1, //スタックの何番目か
"routes":[
{
"routeName":"ScreenA1", //ScreenA1の画面名
"key":"id-1536728255349-2"
},
{
"params":{}, //遷移時にパラメータを指定するとその情報がここに入ります。ヘッダーのタイトルとか。
"routeName":"ScreenA2", //ScreenA2の画面名
"key":"id-1536728255349-9"
},
],
"routeName":"TabAStack"}
ScreenA3が破棄されたので、 routes
からその情報が消えました。
###(余談)各画面のnavigationOptionsで指定することはできない
これまでの例では、stackNavigator全体のnavigationOptions
でタブバーの表示/非表示を切り替えていましたが、 以下のように、stackNavigator全体ではなく各画面のnavigationOptionsを指定する方法では上手く行きません。
const TabAStack = createStackNavigator({
ScreenA1: {
screen: ScreenA1,
navigationOptions: { tabBarVisible: true },
},
ScreenA2: {
screen: ScreenA2,
navigationOptions: { tabBarVisible: false },//タブバー非表示にしたい
},
ScreenA3: {
screen: ScreenA3,
navigationOptions: { tabBarVisible: false },//タブバー非表示にしたい
},
});
なぜなら、この時のnavigationOptionsはTabAStackにしかかかっておらず、bottomNavigatorの方にはかかっていないためです。
また、以下も上手く行きませんでした。
const TabAStack = createStackNavigator({
ScreenA1,
ScreenA2, //タブバー非表示にしたい
ScreenA3, //タブバー非表示にしたい
},{
navigationOptions : ({ navigation }) => ({
//スタックの一番最初だけタブバーを表示する
tabBarVisible: navigation.state.index === 0,
});
});
そもそも、タブ毎にstackNavigatorに突っ込まない方が良いらしい
この記事を書いている途中で気が付いたのですが、
React Navigationのこの箇所に、以下のようにありました
Another option here would be to add another stack navigator as a parent of the tab navigator, and put the details screen there. This is recommended.
タブバーのなかにstackNavigatorをつっこむのではなく、tabNavigatorとそれ以外のタブバー要らない画面をstackNavigatorにつっこむと。
そして、 This is recommended. とあります。
import {createStackNavigator, createBottomTabNavigator} from 'react-navigation'
const TabNavigator = createBottomTabNavigator({
ScreenA1,
TabB,
TabC
});
export default createStackNavigator({
TabNavigator,
ScreenA2, //タブナビゲータの外なのでタブバー表示されない
ScreenA3, //同上
})
確かに、スッキリしますね。
ただ、TabBやTabCにも同じようにタブバー非表示の導線がある場合、この方法だとごっちゃになる気がしました。
const TabNavigator = createBottomTabNavigator({
ScreenA1,
ScreenB1,
ScreenC1,
});
export default createStackNavigator({
TabNavigator,
ScreenA2,
ScreenA3,
ScreenB2,
ScreenB3,
ScreenC2,
ScreenC3,
})
こういう構成のアプリはあまり無いのかもしれないですが、個人的には、それぞれstackNavigatorで囲ってあげる方が、「ScreenA2とScreenA3はScreenA1からしか遷移しない」など体的な導線が分かりやすいのかなぁと思いました。
参考URL
以下を参考にしました。