跳至主要内容
版本:6.x

支持安全区域

默认情况下,React Navigation 尝试确保导航器的元素在有凹槽的设备(例如 iPhone X)上正确显示,以及可能会与应用程序内容重叠的 UI 元素。这些项目包括

  • 物理凹槽
  • 状态栏覆盖
  • iOS 上的首页活动指示器
  • Android 上的导航栏

未被这些项目覆盖的区域称为“安全区域”。

我们尝试在导航器的 UI 元素上应用适当的内边距,以避免被这些项目覆盖。目标是 (a) 最大限度地利用屏幕 (b) 不会隐藏内容或因被物理显示屏切口或某些操作系统 UI 遮挡而难以交互。

虽然 React Navigation 默认情况下会为内置 UI 元素处理安全区域,但您自己的内容可能也需要处理它,以确保内容不会被这些项目隐藏。

用一个带有填充的容器包装整个应用程序来解决 (a) 很诱人,这样可以确保所有内容都不会被遮挡。但这样做会在屏幕上浪费很多空间,如下图左侧所示。我们理想的情况是右侧所示的图像。

Notch on the iPhone X

虽然 React Native 导出了一个 SafeAreaView 组件,但该组件仅支持 iOS 10+,不支持旧版 iOS 或 Android。此外,它也有一些问题,例如,如果包含安全区域的屏幕正在动画,会导致跳跃行为。因此,我们建议使用 react-native-safe-area-context 库中的 useSafeAreaInsets 钩子以更可靠的方式处理安全区域。

警告

react-native-safe-area-context 库也导出了一个 SafeAreaView 组件。虽然它在 Android 上有效,但它也存在与动画时跳跃行为相关的问题。因此,我们建议始终使用 useSafeAreaInsets 钩子,并避免使用 SafeAreaView 组件。

本指南的其余部分将提供有关如何在 React Navigation 中支持安全区域的更多信息。

隐藏/自定义标题或标签栏

Default React Navigation Behavior

React Navigation 在默认标题中处理安全区域。但是,如果您使用的是自定义标题,则必须确保您的 UI 在安全区域内。

例如,如果我没有为headertabBar渲染任何内容,则不会渲染任何内容。

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>
);
}

Text hidden by iPhoneX UI elements

要解决此问题,您可以在内容上应用安全区域内边距。这可以通过使用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中。

Content spaced correctly with safe area insets

这将检测应用程序是否在有凹口的设备上运行,如果是,则确保内容不会隐藏在任何硬件元素后面。

横向模式

即使您使用默认的导航栏和标签栏 - 如果您的应用程序在横向模式下工作,重要的是要确保您的内容不会隐藏在传感器集群后面。

App in landscape mode with text hidden

要解决此问题,您可以再次将安全区域内边距应用于您的内容。这不会与导航栏或标签栏在纵向模式下的默认行为冲突。

App in landscape mode with text visible

使用钩子进行更多控制

在某些情况下,您可能需要对应用哪些填充进行更多控制。例如,您可以通过更改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>
);
}

类似地,您可以在FlatListcontentContainerStyle中应用这些填充,以使内容避开安全区域,但在滚动时仍显示在状态栏和导航栏下方。

总结

  • 使用react-native-safe-area-context中的useSafeAreaInsets钩子,而不是SafeAreaView组件。
  • 不要将整个应用程序包装在SafeAreaView中,而是将样式应用于屏幕内的内容。
  • 使用useSafeAreaInsets钩子仅应用特定内边距,以进行更多控制。