react-native-safe-area-context 主要用于處理異形屏的適配 ,React Navigation 的適配就是使用該組件進(jìn)行處理的。 React Navigation V5 版是通過(guò) safeAreaInsets 屬性進(jìn)行設(shè)置的,新版將該功能提取出來(lái),通過(guò) 組件、Hook 方式處理,這樣做的好處是:無(wú)需自行監(jiān)聽(tīng)屏幕旋轉(zhuǎn),會(huì)自動(dòng)更新同步渲染界面,處理起來(lái)也更加靈活。
安裝
yarn add react-native-safe-area-context
npx pod-install
使用
SafeAreaProvider
這是一個(gè)提供者,本身不會(huì)對(duì)布局產(chǎn)生任何影響,但只有在該組件包裹下的子組件才能使用 react-native-safe-area-context 提供的功能,通常,可以直接包裹在根組件上。React Navigation 本身已經(jīng)使用該組件作了包裹,所以在配合 React Navigation 使用時(shí),無(wú)需再進(jìn)行包裹了,直接使用即可。獨(dú)立使用時(shí),可使用類似如下的代碼:
import { SafeAreaProvider } from 'react-native-safe-area-context';
function App() {
// 通??梢栽?APP 最外層使用,也可以在深層使用,但只有子組件才能API
// 注意:不要把該組件放到有動(dòng)畫或滾動(dòng)的組件下級(jí),比如 Animated 或 ScrollView
// 支持 View 的所有屬性,并支持額外的一個(gè) initialMetrics 屬性
return <SafeAreaProvider initialMetrics={null}>...</SafeAreaProvider>;
}
initialWindowMetrics
上面 SafeAreaProvider 的 initialMetrics 屬性需要提供一個(gè) Object 值,提供相關(guān)的尺寸位置信息,默認(rèn)為自動(dòng)獲取,無(wú)需提供。默認(rèn)提供的信息可以通過(guò)該 Hook 獲取
import { initialWindowMetrics } from 'react-native-safe-area-context';
function HookComponent() {
// 數(shù)據(jù)格式
// {
// frame: { x: number, y: number, width: number, height: number },
// insets: { top: number, left: number, right: number, bottom: number },
// }
const insets = initialWindowMetrics();
...
}
useSafeAreaFrame / SafeAreaFrameContext
獲取離當(dāng)前組件最近的 SafeAreaProvider 尺寸信息
import {
useSafeAreaFrame,
SafeAreaFrameContext
} from 'react-native-safe-area-context';
// 函數(shù)式組件
function HookComponent() {
// 獲取 SafeAreaProvider 的寬高、偏移 x,y
const {x, y, width, height} = useSafeAreaFrame();
// ...
}
// class 組件
class ClassComponent extends React.Component {
render() {
return (
<SafeAreaFrameContext.Consumer>
{(frame) => <View ... />}
</SafeAreaFrameContext.Consumer>
);
}
}
useSafeAreaInsets / SafeAreaInsetsContext
獲取當(dāng)前屏幕異形部分的尺寸
import {
useSafeAreaInsets,
SafeAreaInsetsContext
} from 'react-native-safe-area-context';
// 函數(shù)式組件
function HookComponent() {
// 該 Hook 返回屏幕四個(gè)方向上異形的尺寸
// 若屏幕旋轉(zhuǎn),該值也會(huì)自動(dòng)更新,促使組件同步更新
const {left, right, top, bottom} = useSafeAreaInsets();
return <View style={{ paddingBottom: Math.max(bottom, 16) }} />;
}
// class 組件
class ClassComponent extends React.Component {
render() {
return (
<SafeAreaInsetsContext.Consumer>
{(insets) => <View style={{ paddingTop: insets.top }} />}
</SafeAreaInsetsContext.Consumer>
);
}
}
withSafeAreaInsets
上面兩組分別是使用 Hook / Context 方式獲取相關(guān)尺寸數(shù)值應(yīng)用到 Function、Class 組件,對(duì)于 AreaInsets 還可使用 withSafeAreaInsets 應(yīng)用到高階組件。
class MyConmpoent extends React.Component {
render() {
const {insets} = this.props;
return <View style={{ paddingTop: insets.top }} />
}
}
export default withSafeAreaInsets(MyConmpoent);
SafeAreaView
以上都是比較靈活的方式,自行獲取數(shù)值進(jìn)行處理。還有另外一種較為方便的方式,在 SafeAreaProvider 任何層級(jí)內(nèi)都可以使用 SafeAreaView,該組件與 View 相同,默認(rèn)添加了 padding 屬性,在 View 四周添加了空白用以避開(kāi)屏幕異形的部分,該組件會(huì)在屏幕旋轉(zhuǎn)、或數(shù)值發(fā)生變動(dòng)時(shí)自動(dòng)更新。
import { SafeAreaView } from 'react-native-safe-area-context';
function SomeComponent() {
// 支持 edges / mode 兩個(gè)屬性
// edges: 設(shè)置要添加空白的方向,數(shù)組形式
// mode:添加空白的方式,支持 padding(默認(rèn)) / margin
return (
<SafeAreaView
style={{ flex: 1, backgroundColor: 'red' }}
edges={['top', 'bottom', 'right', 'left']}
mode="padding"
>
<View style={{ flex: 1, backgroundColor: 'blue' }} />
</SafeAreaView>
);
}
圖解
react-native-safe-area-context 返回屏幕異形尺寸,針對(duì)的是“非矩形”的部分,而不是“劉?!?、“針孔”。如下圖,倒腳部分都算作異形,在豎屏?xí)r通常沒(méi)啥問(wèn)題,但在橫屏?xí)r可能不符合設(shè)計(jì)預(yù)期,需注意。

以 React Navigation 舉例, 該組件已內(nèi)置 react-native-safe-area-context,并在 Header、BottomTab組件中處理了 top / bottom 方向的異形屏。在使用 React Navigation 時(shí),如果是一般情況,在頁(yè)面組件中無(wú)需刻意處理,如下圖

但碰到以下兩種情況時(shí),仍需手動(dòng)處理
- 頁(yè)面組件未使用
Header或BottomTab,這就與上面的普通組件沒(méi)什么區(qū)別了,很好理解 - 頁(yè)面支持橫屏,在橫屏?xí)r,如未做任何處理,效果如下圖

如果將頁(yè)面組件包裹在 SafeAreaView 組件內(nèi),top / bottom 方向異形尺寸為 0,left / right 方向會(huì)自動(dòng)應(yīng)用 padding,效果如下圖,但很有可能,下圖也不一定符合設(shè)計(jì)預(yù)期,比如 left 方向,圓角異形導(dǎo)致了 padding,但同時(shí)也浪費(fèi)了不少可視區(qū)域,可通過(guò) SafeAreaView 的 edges 屬性進(jìn)行設(shè)置。
