gaowei1012 / blog

this is blog
2 stars 0 forks source link

react native 适配状态栏方案 #72

Open gaowei1012 opened 3 years ago

gaowei1012 commented 3 years ago

适配 React Native 状态栏

自己写了一个组件,方便后面维护 image

gaowei1012 commented 3 years ago
import React, {useContext} from 'react';

import {PageWraperContext} from './context';
import AndroidContainerFullScreenMode from './AndroidContainerFullScreenMode';
import AndroidContainerNormalMode from './AndroidContainerNormalMode';

export default function AndroidContainer() {
  const {isFullScreenPageMode} = useContext(PageWraperContext);

  return (
    <>
      {isFullScreenPageMode ? (
        <AndroidContainerFullScreenMode />
      ) : (
        <AndroidContainerNormalMode />
      )}
    </>
  );
}
gaowei1012 commented 3 years ago

/* eslint-disable react-native/no-inline-styles */
/* eslint-disable no-shadow */
import React, {useContext} from 'react';
import {View, StatusBar} from 'react-native';

import {PageWraperContext} from './context';
import {useGetPortraitState} from './useGetPortraitState';

export default function AndroidContinerFullScreenMode() {
  const {
    children,
    header,
    isFullScreen,
    isLandScapeAutoHiddenStatusBar,
    statusBarColor,
    reactNativeStatusBarProps,
  } = useContext(PageWraperContext);

  const isPortrait = useGetPortraitState();

  function handleHiddenStatusBar(isFullScreen) {
    let isHiddenStatusBar = isFullScreen;
    if (!isPortrait && isLandScapeAutoHiddenStatusBar) {
      isHiddenStatusBar = true;
    }
    return isHiddenStatusBar;
  }

  const isHiddenStatusBar = handleHiddenStatusBar(isFullScreen);

  return (
    <>
      <View style={{flex: 1}}>{children}</View>
      <>
        <StatusBar
          translucent={true}
          hidden={isHiddenStatusBar}
          backgroundColor={statusBarColor}
          {...reactNativeStatusBarProps}
        />
        {isFullScreen ? (
          <View
            style={{
              position: 'absolute',
              paddingTop: isHiddenStatusBar ? 0 : StatusBar.currentHeight,
              width: '100%',
              backgroundColor: statusBarColor,
            }}>
            {header}
          </View>
        ) : null}
      </>
    </>
  );
}
gaowei1012 commented 3 years ago

import React, {useContext} from 'react';
import {View, StatusBar, StyleSheet} from 'react-native';

import {PageWraperContext} from './context';
import {useGetPortraitState} from './useGetPortraitState';

const statusBarHeight = StatusBar.currentHeight;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  childrenConatiner: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

export default function AndroidContainerNormalMode() {
  const {
    children,
    header,
    statusBarColor,
    reactNativeStatusBarProps,
    isLandScapeAutoHiddenStatusBar,
  } = useContext(PageWraperContext);

  const isPortrait = useGetPortraitState();

  /**
   * 在普通模式下,状态栏只有在用户设定了在横屏的时候自动隐藏状态栏时,状态栏才会在横屏的时候被隐藏
   * 其它情况下将一直显示状态栏
   */
  const isHiddenStatusBar = isLandScapeAutoHiddenStatusBar && !isPortrait;

  const containerStyle = [
    styles.container,
    {
      backgroundColor: statusBarColor,
      paddingTop: isHiddenStatusBar ? 0 : statusBarHeight,
    },
  ];
  return (
    <View style={containerStyle}>
      <StatusBar
        translucent={false}
        hidden={isHiddenStatusBar || false}
        backgroundColor={statusBarColor}
        {...reactNativeStatusBarProps}
      />
      {header}
      <View style={styles.childrenConatiner}>{children}</View>
    </View>
  );
}
gaowei1012 commented 3 years ago
import React, {useContext} from 'react';
import {View, StyleSheet, StatusBar} from 'react-native';

import {PageWraperContext} from './context';
import IosContainerFullScreenMode from './IosContainerFullScreenMode';
import IosContainerNormalMode from './IosContainerNormalMode';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
  },
});

export default function IosContainer() {
  const {
    isFullScreenPageMode,
    isFullScreen,
    reactNativeStatusBarProps,
  } = useContext(PageWraperContext);

  return (
    <View style={styles.container}>
      <StatusBar
        barStyle="dark-content"
        hidden={isFullScreen}
        {...reactNativeStatusBarProps}
      />
      {isFullScreenPageMode ? (
        <IosContainerFullScreenMode />
      ) : (
        <IosContainerNormalMode />
      )}
    </View>
  );
}
gaowei1012 commented 3 years ago
/* eslint-disable react-native/no-inline-styles */
import React, {useContext} from 'react';
import {View, StyleSheet, SafeAreaView} from 'react-native';

import {PageWraperContext} from './context';
import {useGetPortraitState} from './useGetPortraitState';

const styles = StyleSheet.create({
  saveAreaContainer: {
    flex: 1,
    width: '100%',
    position: 'absolute',
  },
  childrenContainer: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

export default function IosFullScreenModeContainer() {
  const isPortraitState = useGetPortraitState();
  const {children, header, isFullScreen, statusBarColor} = useContext(
    PageWraperContext,
  );
  return (
    <>
      <View style={styles.childrenContainer}>{children}</View>
      {
        // 当处于横屏状态,并且没有隐藏状态栏的时候,显示的头部
        !isPortraitState && !isFullScreen ? (
          <View style={{position: 'absolute', width: '100%'}}>{header}</View>
        ) : null
      }

      {
        // 当处于横屏状态,并且没有隐藏状态栏的时候,显示的头部
        isPortraitState && !isFullScreen ? (
          <SafeAreaView
            style={[
              styles.saveAreaContainer,
              {backgroundColor: statusBarColor},
            ]}>
            {header}
          </SafeAreaView>
        ) : null
      }
    </>
  );
}
gaowei1012 commented 3 years ago
import React, {useContext} from 'react';
import {SafeAreaView, StyleSheet} from 'react-native';

import {PageWraperContext} from './context';

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

export default function IosContainerNormalMode() {
  const {children, header, safeAreaViewStyle, statusBarColor} = useContext(
    PageWraperContext,
  );

  return (
    <SafeAreaView
      style={[
        styles.container,
        {backgroundColor: statusBarColor},
        safeAreaViewStyle,
      ]}>
      {header}
      {children}
    </SafeAreaView>
  );
}
gaowei1012 commented 3 years ago

import {useEffect, useState} from 'react';
import {Dimensions} from 'react-native';

export function useGetPortraitState() {
  // 根据当前高度判断是否横屏
  function isPortrait(height, width) {
    let portraitState = true;
    if (height < width) {
      portraitState = false;
    }
    return portraitState;
  }

  const [portraitState, setPortraitState] = useState(() => {
    const {width, height} = Dimensions.get('window');
    return isPortrait(height, width);
  });

  useEffect(() => {
    function handleDimensionsChange({window}) {
      const {width, height} = window;
      setPortraitState(isPortrait(height, width));
    }

    Dimensions.addEventListener('change', handleDimensionsChange);

    return () => {
      Dimensions.removeEventListener('change', handleDimensionsChange);
    };
  });

  return portraitState;
}
gaowei1012 commented 3 years ago
import React from 'react';

export const PageWraperContext = React.createContext({
  isFullScreen: false,
  isLandScapeAutoHiddenStatusBar: false,
  header: <></>,
  statusBarColor: '#fff',
  isFullScreenPageMode: false,
  children: <></>,
  reactNativeStatusBarProps: {},
  safeAreaViewStyle: {},
});
gaowei1012 commented 3 years ago

/* eslint-disable no-shadow */
import React from 'react';
import {Platform} from 'react-native';

import {PageWraperContext} from './context';
import AndroidContainer from './AndroidContainer';
import IosContainer from './IosContainer';

const isIos = Platform.OS === 'ios';

export function PageWrapper({
  children,
  isFullScreen = false,
  isLandScapeAutoHiddenStatusBar = false,
  header = null,
  statusBarColor,
  isFullScreenPageMode = false,
  reactNativeStatusBarProps = {},
  safeAreaViewStyle = {},
}) {
  function getReactNativeStatusBarProps(reactNativeStatusBarProps) {
    const newProps = Object.assign({}, reactNativeStatusBarProps);
    if (newProps.hidden) {
      delete newProps.hidden;
    }
    return newProps;
  }

  const value = {
    children,
    isFullScreen,
    isLandScapeAutoHiddenStatusBar,
    header,
    statusBarColor,
    isFullScreenPageMode,
    reactNativeStatusBarProps: getReactNativeStatusBarProps(
      reactNativeStatusBarProps,
    ),
    safeAreaViewStyle,
  };

  return (
    <>
      <PageWraperContext.Provider value={value}>
        {isIos ? <IosContainer /> : <AndroidContainer />}
      </PageWraperContext.Provider>
    </>
  );
}

PageWrapper.defaultProps = {
  isFullScreen: false,
  isLandScapeAutoHiddenStatusBar: false,
  header: null,
  isFullScreenPageMode: false,
};
gaowei1012 commented 3 years ago

使用


// 引入
import {PageWrapper} from '../../components/SafeAreaViewPlus';

<PageWrapper
      header={<Header title="首页" style={{backgroundColor: '#F7C659'}} />}
      statusBarColor="#F7C659"
      isLandScapeAutoHiddenStatusBar={true}
      reactNativeStatusBarProps={{
        barStyle: 'light-content',
      }}>
      <View
        style={{
          flex: 1,
          justifyContent: 'center',
          alignItems: 'center',
          backgroundColor: '#fff',
        }}>
        <Text>NormalScreenMode</Text>

        <Button title="Go Back" onPress={() => props.navigation.goBack()} />
      </View>
    </PageWrapper>
gaowei1012 commented 3 years ago

效果图 image