反应NativeでiPadの画面分割に対応させる方法

React Nativeでこれをやるには少々问题があって, Dimensionsがうまくウインドウのサイズを返してくれない。“ Split View”でも“ Slide Over”でも常に同じ値を返す:

 console.log(Dimensions.get('screen')) // {fontScale: 1, width: 768, height: 1024, scale: 2} 
console.log(Dimensions.get('window')) // {fontScale: 1, width: 768, height: 1024, scale: 2}

それは,最も外侧にViewを一枚敷いて, flex: 1をスタイルに设定して, onLayoutにイベントハンドラを设定を,そのビューのサイズを取得することだ。そしてそのサイズuxReduxのストアなりReactのContextなりに保存しておく。

适应性布局提供商/消费者

分いわゆるProvider-Consumerパターンの形を取る。サイズデータはReactのContext APIは使わずReduxのストアに格纳する。

提供者

 // @flow 
// adaptable-layout-provider.js
 import * as React from 'react' 
import { View, StyleSheet } from 'react-native'
import { compose, withHandlers, pure, type HOC } from 'recompose'
import actions from '../actions'
import withDispatch from '../utils/with-dispatch'
 /** 
*
* (FlatList is just wrapper for View)
*
* @see https://facebook.github.io/react-native/docs/view.html#onlayout
*/
export type OnLayout = {|
nativeEvent: {|
layout: {|
x: number,
y: number,
width: number,
height: number
|}
|}
|}
 type Props = { 
children: React.Node
}
 const enhance: HOC = compose( 
withDispatch(),
pure,
withHandlers({
emitDimensionChanges: props => (event: OnLayout) => {
const { dispatch } = props
const { width, height } = event.nativeEvent.layout
  dispatch(actions.viewport.update({ width, height })) 
}
})
)
 const Provider = enhance(props => ( 

{props.children}

))
 export default Provider 
 const styles = StyleSheet.create({ 
container: {
flex: 1
}
})

消费者

 // @flow 
// adaptable-layout-consumer.js
 import * as React from 'react' 
import { compose, pure, type HOC } from 'recompose'
import connect from '../utils/connect-store'
 type Props = { 
renderOnWide?: React.Node,
renderOnNarrow?: React.Node
}
 const enhance: HOC = compose( 
connect(({ viewport }) => ({ viewport })),
pure
)
 const Consumer = enhance(props => { 
const { viewport } = props
// may return nothing:
// 1. renderOnWide set but we have narrow layout
// 2. renderOnNarrow set but we have wide layout
let children = null
const wideLayout = viewport.isTablet
  if (wideLayout === true && props.renderOnWide) { 
children = props.renderOnWide
} else if (wideLayout === false && props.renderOnNarrow) {
children = props.renderOnNarrow
}
  return children 
})
 export default Consumer 

减速器

 // @flow 
// reducers/viewport.js
import type { ViewportActionType } from '../actions/viewport'
import * as viewportActions from '../actions/viewport'
import { Dimensions } from 'react-native'
 export type Dimension = { 
width: number,
height: number
}
 export type ViewportState = { 
width: number,
height: number,
isLandscape: boolean,
isPortrait: boolean,
isTablet: boolean,
isPhone: boolean
}
 function isLandscape(dim: Dimension) { 
return dim.width >= dim.height
}
 function isTablet(dim: Dimension) { 
return dim.width >= 1024
}
 const dim: Dimension = Dimensions.get('window') 
export const initialViewportState: ViewportState = {
width: dim.width,
height: dim.height,
isLandscape: isLandscape(dim),
isPortrait: !isLandscape(dim),
isTablet: isTablet(dim),
isPhone: !isTablet(dim)
}
 export default function viewport( 
state: ViewportState = initialViewportState,
action: ViewportActionType
): ViewportState {
switch (action.type) {
case viewportActions.VIEWPORT_UPDATE:
const dim = action.payload
return {
...action.payload,
isLandscape: isLandscape(dim),
isPortrait: !isLandscape(dim),
isTablet: isTablet(dim),
isPhone: !isTablet(dim)
}
default:
return state || initialViewportState
}
}

行动

 // @flow 
import { type Dimension } from '../reducers/viewport'
export const VIEWPORT_UPDATE = 'VIEWPORT_UPDATE'
 export type ViewportActionType = { 
type: 'VIEWPORT_UPDATE',
payload: Dimension
}
 export function update(dim: Dimension) { 
return {
type: VIEWPORT_UPDATE,
payload: dim
}
}

Reduxのローバル変数量に格纳してもいい。おすすめはしないけど。自分のアプリのアーククチャに合った方法で导入されたい。

如何使用它

ルートビューのコンポーネントにて:

 const RootView = () => ( 



)

画面コンポーネントにて:

 const MainScreen = props => { 
return (
<AdaptableLayoutConsumer
renderOnNarrow={

}
renderOnWide={

}
/>
)
}

希望有帮助!