支持安全区域
默认情况下,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
中。
这将检测应用程序是否在有凹口的设备上运行,如果是,则确保内容不会隐藏在任何硬件元素后面。
横向模式
即使您使用默认的导航栏和标签栏 - 如果您的应用程序在横向模式下工作,重要的是要确保您的内容不会隐藏在传感器集群后面。
要解决此问题,您可以再次将安全区域内边距应用于您的内容。这不会与导航栏或标签栏在纵向模式下的默认行为冲突。
使用钩子进行更多控制
在某些情况下,您可能需要对应用哪些填充进行更多控制。例如,您可以通过更改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
中应用这些填充,以使内容避开安全区域,但在滚动时仍显示在状态栏和导航栏下方。
总结
- 使用
react-native-safe-area-context
中的useSafeAreaInsets
钩子,而不是SafeAreaView
组件。 - 不要将整个应用程序包装在
SafeAreaView
中,而是将样式应用于屏幕内的内容。 - 使用
useSafeAreaInsets
钩子仅应用特定内边距,以进行更多控制。