セーフエリアのサポート
デフォルトでは、React Navigationは、ノッチ付きデバイス(例:iPhone X)やアプリのコンテンツに重なる可能性のあるUI要素で、ナビゲーターの要素が正しく表示されるようにします。このようなアイテムには以下が含まれます。
- 物理的なノッチ
- ステータスバーのオーバーレイ
- iOSのホームアクティビティインジケーター
- Androidのナビゲーションバー
このようなアイテムと重ならない領域は、「セーフエリア」と呼ばれます。
私たちは、ナビゲーターのUI要素に適切なインセットを適用して、そのようなアイテムと重ならないように努めています。目標は、(a) 画面の使用を最大化すること、(b) コンテンツを隠したり、物理的なディスプレイの切り欠きや一部のオペレーティングシステムのUIによって隠されて操作を困難にしたりすることなく、画面の使用を最大化することです。
React Navigationは、組み込みのUI要素のセーフエリアをデフォルトで処理しますが、コンテンツがこれらのアイテムによって隠されないようにするために、独自のコンテンツもそれを処理する必要がある場合があります。
すべてのコンテンツが隠されないように、アプリ全体をパディング付きのコンテナで囲むことで(a)を解決したくなるかもしれません。しかし、そうすることで、下の左側の画像に示すように、画面上の多くのスペースを無駄にしてしまいます。理想的には、右側の画像に示すようにしたいと考えています。
React Nativeは`SafeAreaView`コンポーネントをエクスポートしますが、このコンポーネントはiOS 10+のみをサポートし、古いiOSバージョンやAndroidはサポートしていません。さらに、セーフエリアを含む画面がアニメーション化されている場合、ぎくしゃくした動作を引き起こすなど、いくつかの問題もあります。そのため、 react-native-safe-area-context ライブラリの`useSafeAreaInsets`フックを使用して、より信頼性の高い方法でセーフエリアを処理することをお勧めします。
`react-native-safe-area-context`ライブラリも`SafeAreaView`コンポーネントをエクスポートします。Androidでは動作しますが、アニメーション時にぎくしゃくした動作に関連する同じ問題があります。そのため、常に`useSafeAreaInsets`フックを使用し、`SafeAreaView`コンポーネントの使用は避けることをお勧めします。
このガイドの残りの部分では、React Navigationでセーフエリアをサポートする方法について詳しく説明します。
非表示/カスタムヘッダーまたはタブバー
React Navigationはデフォルトのヘッダーでセーフエリアを処理します。ただし、カスタムヘッダーを使用している場合は、UIがセーフエリア内にあることを確認することが重要です。
たとえば、`header`または`tabBar`に何もレンダリングしないと、何もレンダリングされません。
import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
function Demo() {
return (
<View
style={{ flex: 1, justifyContent: 'space-between', alignItems: 'center' }}
>
<Text>This is top text.</Text>
<Text>This is bottom text.</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ headerShown: false }}
>
<Stack.Screen name="Home">
{() => (
<Tab.Navigator
initialRouteName="Analitics"
tabBar={() => null}
screenOptions={{ headerShown: false }}
>
<Tab.Screen name="Analitics" component={Demo} />
<Tab.Screen name="Profile" component={Demo} />
</Tab.Navigator>
)}
</Stack.Screen>
<Stack.Screen name="Settings" component={Demo} />
</Stack.Navigator>
</NavigationContainer>
);
}
この問題を解決するには、コンテンツにセーフエリアのインセットを適用します。これは、`react-native-safe-area-context`ライブラリの`useSafeAreaInsets`フックを使用して実現できます。
import {
SafeAreaProvider,
useSafeAreaInsets,
} from 'react-native-safe-area-context';
function Demo() {
const insets = useSafeAreaInsets();
return (
<View
style={{
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
// Paddings to handle safe area
paddingTop: insets.top,
paddingBottom: insets.bottom,
paddingLeft: insets.left,
paddingRight: insets.right,
}}
>
<Text>This is top text.</Text>
<Text>This is bottom text.</Text>
</View>
);
}
export default function App() {
return (
<SafeAreaProvider>
<NavigationContainer>{/*(...) */}</NavigationContainer>
</SafeAreaProvider>
);
}
こちらの指示に従って、アプリを`SafeAreaProvider`でラップしてください。
これにより、アプリがノッチ付きデバイスで実行されているかどうかが検出され、実行されている場合は、コンテンツがハードウェア要素の背後に隠れないようになります。
ランドスケープモード
デフォルトのナビゲーションバーとタブバーを使用している場合でも、アプリケーションがランドスケープモードで動作する場合は、コンテンツがセンサー cluster の背後に隠れないようにすることが重要です。
これを修正するには、もう一度、コンテンツにセーフエリアのインセットを適用します。これは、ポートレートモードでのナビゲーションバーやタブバーのデフォルトの動作と競合しません。
より詳細な制御のためにフックを使用する
場合によっては、どのパディングを適用するかをより詳細に制御する必要があるかもしれません。たとえば、`style`オブジェクトを変更することで、上下のパディングのみを適用できます。
import { useSafeAreaInsets } from 'react-native-safe-area-context';
function Demo() {
const insets = useSafeAreaInsets();
return (
<View
style={{
paddingTop: insets.top,
paddingBottom: insets.bottom,
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Text>This is top text.</Text>
<Text>This is bottom text.</Text>
</View>
);
}
同様に、`FlatList`の`contentContainerStyle`にこれらのパディングを適用して、コンテンツがセーフエリアを回避するようにしながら、スクロール時にステータスバーとナビゲーションバーの下に表示することもできます。
まとめ
- `SafeAreaView`コンポーネントの代わりに`react-native-safe-area-context`の`useSafeAreaInsets`フックを使用する
- アプリ全体を`SafeAreaView`でラップするのではなく、画面内のコンテンツにスタイルを適用する
- より詳細な制御のために`useSafeAreaInsets`フックを使用して特定のインセットのみを適用する