Skip to main content
Version: 7.x

支持安全区域

默认情况下,React Navigation 尝试确保导航器的元素在带有凹口的设备(例如 iPhone X)和可能与应用内容重叠的 UI 元素上正确显示。此类项目包括:

¥By default, React Navigation tries to ensure that the elements of the navigators display correctly on devices with notches (e.g. iPhone X) and UI elements which may overlap the app content. Such items include:

  • 物理凹口

    ¥Physical notches

  • 状态栏覆盖

    ¥Status bar overlay

  • iOS 上的家庭活动指示器

    ¥Home activity indicator on iOS

  • Android 上的导航栏

    ¥Navigation bar on Android

没有被这些项目重叠的区域被称为 "安全区"。

¥The area not overlapped by such items is referred to as "safe area".

我们尝试在导航器的 UI 元素上应用适当的插图,以避免与此类项目重叠。目标是 (a) 最大限度地利用屏幕 (b) 不隐藏内容或通过物理显示切口或某些操作系统 UI 遮挡内容来使其难以交互。

¥We try to apply proper insets on the UI elements of the navigators to avoid being overlapped by such items. The goal is to (a) maximize usage of the screen (b) without hiding content or making it difficult to interact with by having it obscured by a physical display cutout or some operating system UI.

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

¥While React Navigation handles safe areas for the built-in UI elements by default, your own content may also need to handle it to ensure that content isn't hidden by these items.

通过将整个应用封装在一个带有填充的容器中来解决(a)是很诱人的,以确保所有内容都不会被遮挡。但这样做时,我们浪费了屏幕上的大量空间,如下左图所示。我们理想想要的是右图所示的图片。

¥It's tempting to solve (a) by wrapping your entire app in a container with padding that ensures all content will not be occluded. But in doing so, we waste a bunch of space on the screen, as pictured in the image on the left below. What we ideally want is the image pictured on the right.

Notch on the iPhone X

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

¥While React Native exports a SafeAreaView component, this component only supports iOS 10+ with no support for older iOS versions or Android. In addition, it also has some issues, i.e. if a screen containing safe area is animating, it causes jumpy behavior. So we recommend to use the useSafeAreaInsets hook from the react-native-safe-area-context library to handle safe areas in a more reliable way.

警告

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

¥The react-native-safe-area-context library also exports a SafeAreaView component. While it works on Android, it also has the same issues related to jumpy behavior when animating. So we recommend always using the useSafeAreaInsets hook instead and avoid using the SafeAreaView component.

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

¥The rest of this guide gives more information on how to support safe areas in React Navigation.

隐藏/自定义标题或选项卡栏

¥Hidden/Custom Header or Tab Bar

Default React Navigation Behavior

React Navigation 处理默认标题中的安全区域。但是,如果你使用自定义标头,请务必确保你的 UI 位于安全区域内。

¥React Navigation handles safe area in the default header. However, if you're using a custom header, it's important to ensure your UI is within the safe area.

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

¥For example, if I render nothing for the header or tabBar, nothing renders

const MyTabs = createBottomTabNavigator({
initialRouteName: 'Analytics',
tabBar: () => null,
screenOptions: {
headerShown: false,
},
screens: {
Analytics: Demo,
Profile: Demo,
},
});

const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screenOptions: {
headerShown: false,
},
screens: {
Home: MyTabs,
Settings: Demo,
},
});

Try on Snack

Text hidden by iPhoneX UI elements

要解决此问题,你可以在内容上应用安全区域插图。这可以使用 react-native-safe-area-context 库中的 useSafeAreaInsets 钩子来实现:

¥To fix this issue you can apply safe area insets on your content. This can be achieved using the useSafeAreaInsets hook from the react-native-safe-area-context library:

function Demo() {
const insets = useSafeAreaInsets();

return (
<View
style={{
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
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>
);
}

const Navigation = createStaticNavigation(RootStack);

export default function App() {
return (
<SafeAreaProvider>
<Navigation />
</SafeAreaProvider>
);
}
Try on Snack

确保按照 此处 说明将你的应用封装在 SafeAreaProvider 中。

¥Make sure to wrap your app in SafeAreaProvider as per the instructions here.

Content spaced correctly with safe area insets

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

¥This will detect if the app is running on a device with notches, if so, ensure the content isn't hidden behind any hardware elements.

横向模式

¥Landscape Mode

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

¥Even if you're using the default navigation bar and tab bar - if your application works in landscape mode it's important to ensure your content isn't hidden behind the sensor cluster.

App in landscape mode with text hidden

要解决此问题,你可以再次将安全区域插入应用到你的内容。这不会与导航栏或选项卡栏在纵向模式下的默认行为发生冲突。

¥To fix this you can, once again, apply safe area insets to your content. This will not conflict with the navigation bar nor the tab bar's default behavior in portrait mode.

App in landscape mode with text visible

使用钩子进行更多控制

¥Use the hook for more control

在某些情况下,你可能需要更多地控制应用哪些填充。例如,你只能通过更改 style 对象来应用顶部和底部填充:

¥In some cases you might need more control over which paddings are applied. For example, you can only apply the top and the bottom padding by changing the style object:

import {
SafeAreaProvider,
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>
);
}

const Navigation = createStaticNavigation(RootStack);

export default function App() {
return (
<SafeAreaProvider>
<Navigation />
</SafeAreaProvider>
);
}
Try on Snack

同样,你可以在 contentContainerStyleFlatList 中应用这些填充,以使内容避开安全区域,但在滚动时仍将其显示在状态栏和导航栏下方。

¥Similarly, you could apply these paddings in contentContainerStyle of FlatList to have the content avoid the safe areas, but still show them under the statusbar and navigation bar when scrolling.

概括

¥Summary

  • 使用 react-native-safe-area-context 中的 useSafeAreaInsets 钩子代替 SafeAreaView 组件

    ¥Use useSafeAreaInsets hook from react-native-safe-area-context instead of SafeAreaView component

  • 不要将整个应用封装在 SafeAreaView 中,而是将样式应用于屏幕内的内容

    ¥Don't wrap your whole app in SafeAreaView, instead apply the styles to content inside your screens

  • 使用 useSafeAreaInsets 钩子仅应用特定插图以获得更多控制

    ¥Apply only specific insets using the useSafeAreaInsets hook for more control