跳到主要内容
版本:7.x

支持安全区域

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

  • 物理刘海
  • 状态栏覆盖
  • iOS 上的 Home 活动指示器
  • 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 hook,以更可靠的方式处理安全区域。

警告

react-native-safe-area-context 库也导出了 SafeAreaView 组件。虽然它在 Android 上有效,但在垂直动画方面也存在相同的跳跃行为问题。此外,SafeAreaView 组件和 useSafeAreaInsets hook 可能会在不同的时间更新,导致一起使用时出现闪烁。因此,我们建议始终使用 useSafeAreaInsets hook,并避免使用 SafeAreaView 组件以获得一致的行为。

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

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

Default React Navigation Behavior

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

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

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

Snack 上尝试

Text hidden by iPhoneX UI elements

要解决此问题,您可以在内容上应用安全区域内边距。这可以使用 react-native-safe-area-context 库中的 useSafeAreaInsets hook 来实现

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>
);
}
Snack 上尝试

请务必按照此处的说明将您的应用包裹在 SafeAreaProvider 中。

Content spaced correctly with safe area insets

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

横向模式

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

App in landscape mode with text hidden

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

App in landscape mode with text visible

使用 hook 以获得更多控制

在某些情况下,您可能需要更多地控制应用哪些内边距。例如,您可以通过更改 style 对象仅应用顶部和底部内边距

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>
);
}
Snack 上尝试

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

总结

  • 使用 react-native-safe-area-context 中的 useSafeAreaInsets hook 而不是 SafeAreaView 组件
  • 不要将整个应用包裹在 SafeAreaView 中,而是将样式应用于屏幕内部的内容
  • 使用 useSafeAreaInsets hook 仅应用特定的内边距以获得更多控制