支持安全区域
默认情况下,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.
虽然 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
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.
例如,如果我不为 header
或 tabBar
渲染任何内容,则不会渲染任何内容
¥For example, if I render nothing for the header
or tabBar
, nothing renders
- Static
- Dynamic
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,
},
});
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="Analytics"
tabBar={() => null}
screenOptions={{ headerShown: false }}
>
<Tab.Screen name="Analytics" 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
钩子来实现:
¥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:
- Static
- Dynamic
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>
);
}
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>
);
}
export default function App() {
return (
<SafeAreaProvider>
<NavigationContainer>
{/*(...) */}
</NavigationContainer>
</SafeAreaProvider>
);
}
确保按照 此处 说明将你的应用封装在 SafeAreaProvider
中。
¥Make sure to wrap your app in SafeAreaProvider
as per the instructions here.
这将检测应用是否在带有凹口的设备上运行,如果是,请确保内容没有隐藏在任何硬件元素后面。
¥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.
要解决此问题,你可以再次将安全区域插入应用到你的内容。这不会与导航栏或选项卡栏在纵向模式下的默认行为发生冲突。
¥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.
使用钩子进行更多控制
¥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:
- Static
- Dynamic
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>
);
}
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>
);
}
同样,你可以在 contentContainerStyle
或 FlatList
中应用这些填充,以使内容避开安全区域,但在滚动时仍将其显示在状态栏和导航栏下方。
¥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 fromreact-native-safe-area-context
instead ofSafeAreaView
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