简析在React Native中如何适配iPhoneX

2023-12-18 19:58

本文主要是介绍简析在React Native中如何适配iPhoneX,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


一、介绍


iPhone X 发布也有一段时间了,独特的 "齐刘海",以及 "小嘴巴" 带给了苹果粉们无限的遐想,同时也带来众多的吐槽。

前几天,招商银行公众号在微信推送了一条消息,11月招商银行App要发布最新版本,完美适配iPhoneX,是国内第一家银行App适配iPhoneX。感兴趣的朋友可以去下载体验一下。作为App开发者,此时你的心情是欣喜若狂,还是一万个XXX奔腾而过。欣喜也许是因为又可以在自己开发App中"大展拳脚",而一万个XXX奔腾而过,也许完美表达了你的真心,又该乖乖的去做适配了。

扯了这么多,终于上道了。本篇博客内容就是要和大家分享在React Native开发的App中,我们该如何去做适配。首先在做适配之前,我们先了解下iPhoneX在UI上的一些变化。iPhoneX版本引入了一个新名词: 【安全区域】



以上图竖屏为例,安全区域即从顶部传感器之下,底部Home区域之上的可视交互区域。那么和之前的iPhone系列有什么不同呢?




iOS11前屏幕的分辨率为 375 * 667,而iPhoneX屏幕的高度则变为812,顶部高出145。所以适配的问题基本围绕UI来解决,并且适配的核心思路就是:【避开安全区域,使布局自适应】,我们来看几个对比图:


(1)状态栏部分





(2)底部导航部分



(3)横屏状态



二、适配


iOS11前导航栏的高度是64,其中状态栏(StatusBar)的高度为20。iPhoneX的状态栏(StatusBar)高度变为了44(传感器区域高度),如果是自定义的TopBar,这部分需要做相应的适配。
iPhoneX的底部增加了虚拟Home区,由于安全区域的原因默认TabBar的高度由49变为83,增高了34(Home区高度),所以自定义的底部TabBar也需要需改其适配方案。


图片来源:http://fighting300.com/2017/09/14/iOS11-UI-adjust/

解决这个问题,最简单的方式就是给每个界面的顶部布局和底部有导航的布局曾加高度,修改PaddingTop或者PaddingBottom。同时为了iOS11之前同样适用,我们需要根据版本来让系统选择不同的Style即可。所以第一步我们需要判定当前的手机设备是否为iPhoneX。如何判断呢?很简单,可以根据一个很明显的改变:屏幕高度。

[javascript] view plain copy
  1. import {  
  2.     Platform,  
  3.     Dimensions  
  4. } from 'react-native';  
  5.   
  6. // iPhoneX  
  7. const X_WIDTH = 375;  
  8. const X_HEIGHT = 812;  
  9.   
  10. // screen  
  11. const SCREEN_WIDTH = Dimensions.get('window').width;  
  12. const SCREEN_HEIGHT = Dimensions.get('window').height;  
  13.   
  14. export function isIphoneX() {  
  15.     return (  
  16.         Platform.OS === 'ios' &&   
  17.         ((SCREEN_HEIGHT === X_HEIGHT && SCREEN_WIDTH === X_WIDTH) ||   
  18.         (SCREEN_HEIGHT === X_WIDTH && SCREEN_WIDTH === X_HEIGHT))  
  19.     )  
  20. }  

有了上述条件,我们可以根据设备版本来选择不同的Style样式即可。
[javascript] view plain copy
  1. export function ifIphoneX (iphoneXStyle, regularStyle) {  
  2.     if (isIphoneX()) {  
  3.         return iphoneXStyle;  
  4.     } else {  
  5.         return regularStyle  
  6.     }  
  7. }  

然后在你的样式文件中添加样式
[javascript] view plain copy
  1. const styles = StyleSheet.create({  
  2.     topBar: {  
  3.         backgroundColor: '#ffffff',  
  4.         ...ifIphoneX({  
  5.             paddingTop: 44  
  6.         }, {  
  7.             paddingTop: 20  
  8.         })  
  9.     },  
  10. })  


三、扩展


想必大家都知道,React Native 在前两天发布了0.50.1版本。幸运的是,在该版本中,添加了一个SafeAreaView的Component,来完美支持iPhoneX的适配。并且React-Navigation导航控件库也在^1.0.0-beta.16版本添加对iPhoneX的支持。小伙伴们终于可以轻松的燥起来了。此时也会有一个新的问题,不能升级RN版本的童靴怎么办呢?也不用急,React社区react-community开源了一个JsOnly版本的SafeAreaView,使得在低版本上同样可以解决iPhoneX的适配问题,使用方式也很简单:

[html] view plain copy
  1. <SafeAreaView>  
  2.   <View>  
  3.     <Text>Look, I'm safe!</Text>  
  4.   </View>  
  5. </SafeAreaView>  
只要将SafeAreaView作为最外层控件即可。


四、SafeAreaView 核心源码简析


SafeAreaView的index.js文件中的核心代码,分析实现大致分为如下:

(1)测量,设置触摸安全区域

[html] view plain copy
  1.   componentDidMount() {  
  2.     InteractionManager.runAfterInteractions(() => {  
  3.       this._onLayout();  
  4.     });  
  5.   }  
  6.   
  7. .....  
  8.   
  9. _onLayout = () => {  
  10.     if (!this.view) return;  
  11.   
  12.     const { isLandscape } = this.props;  
  13.     const { orientation } = this.state;  
  14.     const newOrientation = isLandscape ? 'landscape' : 'portrait';  
  15.     if (orientation && orientation === newOrientation) {  
  16.       return;  
  17.     }  
  18.   
  19.     const WIDTH = isLandscape ? X_HEIGHT : X_WIDTH;  
  20.     const HEIGHT = isLandscape ? X_WIDTH : X_HEIGHT;  
  21.   
  22.     this.view.measureInWindow((winX, winY, winWidth, winHeight) => {  
  23.       let realY = winY;  
  24.       let realX = winX;  
  25.   
  26.       if (realY >= HEIGHT) {  
  27.         realY = realY % HEIGHT;  
  28.       } else if (realY < 0) {  
  29.         realY = realY % HEIGHT + HEIGHT;  
  30.       }  
  31.   
  32.       if (realX >= WIDTH) {  
  33.         realX = realX % WIDTH;  
  34.       } else if (realX < 0) {  
  35.         realX = realX % WIDTH + WIDTH;  
  36.       }  
  37.   
  38.       const touchesTop = realY === 0;  
  39.       const touchesBottom = realY + winHeight >= HEIGHT;  
  40.       const touchesLeft = realX === 0;  
  41.       const touchesRight = realX + winWidth >= WIDTH;  
  42.   
  43.       this.setState({  
  44.         touchesTop,  
  45.         touchesBottom,  
  46.         touchesLeft,  
  47.         touchesRight,  
  48.         orientation: newOrientation,  
  49.       });  
  50.     });  
  51.   };  

(2)获取设备环境

[html] view plain copy
  1. const isIPhoneX = (() => {  
  2.   if (minor >= 50) {  
  3.     return isIPhoneX_deprecated;  
  4.   }  
  5.   
  6.   return (  
  7.     Platform.OS === 'ios' &&  
  8.     ((D_HEIGHT === X_HEIGHT && D_WIDTH === X_WIDTH) ||  
  9.       (D_HEIGHT === X_WIDTH && D_WIDTH === X_HEIGHT))  
  10.   );  
  11. })();  
  12.   
  13. const isIPad = (() => {  
  14.   if (Platform.OS !== 'ios' || isIPhoneX) return false;  
  15.   
  16.   // if portrait and width is smaller than iPad width  
  17.   if (D_HEIGHT > D_WIDTH && D_WIDTH < PAD_WIDTH) {  
  18.     return false;  
  19.   }  
  20.   
  21.   // if landscape and height is smaller that iPad height  
  22.   if (D_WIDTH > D_HEIGHT && D_HEIGHT < PAD_WIDTH) {  
  23.     return false;  
  24.   }  
  25.   
  26.   return true;  
  27. })();  
  28.   
  29. const statusBarHeight = isLandscape => {  
  30.   if (isIPhoneX) {  
  31.     return isLandscape ? 0 : 44;  
  32.   }  
  33.   
  34.   if (isIPad) {  
  35.     return 20;  
  36.   }  
  37.   
  38.   return isLandscape ? 0 : 20;  
  39. };  

(3)根据设备环境版本,触摸区域,获取对应的Padding样式,并赋值给safeAreaStyle

[html] view plain copy
  1. _getSafeAreaStyle = () => {  
  2.     const { touchesTop, touchesBottom, touchesLeft, touchesRight } = this.state;  
  3.     const { forceInset, isLandscape } = this.props;  
  4.   
  5.     const style = {  
  6.       paddingTop: touchesTop ? this._getInset('top') : 0,  
  7.       paddingBottom: touchesBottom ? this._getInset('bottom') : 0,  
  8.       paddingLeft: touchesLeft ? this._getInset('left') : 0,  
  9.       paddingRight: touchesRight ? this._getInset('right') : 0,  
  10.     };  
  11.   
  12.     if (forceInset) {  
  13.       Object.keys(forceInset).forEach(key => {  
  14.         let inset = forceInset[key];  
  15.   
  16.         if (inset === 'always') {  
  17.           inset = this._getInset(key);  
  18.         }  
  19.   
  20.         if (inset === 'never') {  
  21.           inset = 0;  
  22.         }  
  23.   
  24.         switch (key) {  
  25.           case 'horizontal': {  
  26.             style.paddingLeft = inset;  
  27.             style.paddingRight = inset;  
  28.             break;  
  29.           }  
  30.           case 'vertical': {  
  31.             style.paddingTop = inset;  
  32.             style.paddingBottom = inset;  
  33.             break;  
  34.           }  
  35.           case 'left':  
  36.           case 'right':  
  37.           case 'top':  
  38.           case 'bottom': {  
  39.             const padding = `padding${key[0].toUpperCase()}${key.slice(1)}`;  
  40.             style[padding] = inset;  
  41.             break;  
  42.           }  
  43.         }  
  44.       });  
  45.     }  
  46.   
  47.     return style;  
  48.   };  
  49.   
  50.   _getInset = key => {  
  51.     const { isLandscape } = this.props;  
  52.     switch (key) {  
  53.       case 'horizontal':  
  54.       case 'right':  
  55.       case 'left': {  
  56.         return isLandscape ? (isIPhoneX ? 44 : 0) : 0;  
  57.       }  
  58.       case 'vertical':  
  59.       case 'top': {  
  60.         return statusBarHeight(isLandscape);  
  61.       }  
  62.       case 'bottom': {  
  63.         return isIPhoneX ? (isLandscape ? 24 : 34) : 0;  
  64.       }  
  65.     }  
  66.   };  

(4)将样式传递给顶层布局View,使得布局自使用

[html] view plain copy
  1. class SafeView extends Component {  
  2.   
  3.   componentWillReceiveProps() {  
  4.     this._onLayout();  
  5.   }  
  6.   
  7.   render() {  
  8.     const { forceInset = false, isLandscape, children, style } = this.props;  
  9.   
  10.     if (Platform.OS !== 'ios') {  
  11.       return <View style={style}>{this.props.children}</View>;  
  12.     }  
  13.   
  14.     if (!forceInset && minor >= 50) {  
  15.       return <SafeAreaView style={style}>{this.props.children}</SafeAreaView>;  
  16.     }  
  17.   
  18.     const safeAreaStyle = this._getSafeAreaStyle();  
  19.   
  20.     return (  
  21.       <View  
  22.         ref={c => (this.view = c)}  
  23.         onLayout={this._onLayout}  
  24.         style={[style, safeAreaStyle]}  
  25.       >  
  26.         {this.props.children}  
  27.       </View>  
  28.     );  
  29.   }  
  30. }  

基本的思路都是根据设备环境,以及屏幕状态,设置对应的Padding样式,从而自适应布局即可。



转载http://blog.csdn.net/u013718120/article/details/78488285?locationNum=5&fps=1

这篇关于简析在React Native中如何适配iPhoneX的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509594

相关文章

HTML5的input标签的`type`属性值详解和代码示例

《HTML5的input标签的`type`属性值详解和代码示例》HTML5的`input`标签提供了多种`type`属性值,用于创建不同类型的输入控件,满足用户输入的多样化需求,从文本输入、密码输入、... 目录一、引言二、文本类输入类型2.1 text2.2 password2.3 textarea(严格

SpringBoot返回文件让前端下载的几种方式

《SpringBoot返回文件让前端下载的几种方式》文章介绍了开发中文件下载的两种常见解决方案,并详细描述了通过后端进行下载的原理和步骤,包括一次性读取到内存和分块写入响应输出流两种方法,此外,还提供... 目录01 背景02 一次性读取到内存,通过响应输出流输出到前端02 将文件流通过循环写入到响应输出流

SpringBoot+Vue3整合SSE实现实时消息推送功能

《SpringBoot+Vue3整合SSE实现实时消息推送功能》在日常开发中,我们经常需要实现实时消息推送的功能,这篇文章将基于SpringBoot和Vue3来简单实现一个入门级的例子,下面小编就和大... 目录前言先大概介绍下SSE后端实现(SpringBoot)前端实现(vue3)1. 数据类型定义2.

前端Visual Studio Code安装配置教程之下载、汉化、常用组件及基本操作

《前端VisualStudioCode安装配置教程之下载、汉化、常用组件及基本操作》VisualStudioCode是微软推出的一个强大的代码编辑器,功能强大,操作简单便捷,还有着良好的用户界面,... 目录一、Visual Studio Code下载二、汉化三、常用组件1、Auto Rename Tag2

vite搭建vue3项目的搭建步骤

《vite搭建vue3项目的搭建步骤》本文主要介绍了vite搭建vue3项目的搭建步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1.确保Nodejs环境2.使用vite-cli工具3.进入项目安装依赖1.确保Nodejs环境

Nginx搭建前端本地预览环境的完整步骤教学

《Nginx搭建前端本地预览环境的完整步骤教学》这篇文章主要为大家详细介绍了Nginx搭建前端本地预览环境的完整步骤教学,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录项目目录结构核心配置文件:nginx.conf脚本化操作:nginx.shnpm 脚本集成总结:对前端的意义很多

前端缓存策略的自解方案全解析

《前端缓存策略的自解方案全解析》缓存从来都是前端的一个痛点,很多前端搞不清楚缓存到底是何物,:本文主要介绍前端缓存的自解方案,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、为什么“清缓存”成了技术圈的梗二、先给缓存“把个脉”:浏览器到底缓存了谁?三、设计思路:把“发版”做成“自愈”四、代码

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter

Vue3视频播放组件 vue3-video-play使用方式

《Vue3视频播放组件vue3-video-play使用方式》vue3-video-play是Vue3的视频播放组件,基于原生video标签开发,支持MP4和HLS流,提供全局/局部引入方式,可监听... 目录一、安装二、全局引入三、局部引入四、基本使用五、事件监听六、播放 HLS 流七、更多功能总结在 v

JS纯前端实现浏览器语音播报、朗读功能的完整代码

《JS纯前端实现浏览器语音播报、朗读功能的完整代码》在现代互联网的发展中,语音技术正逐渐成为改变用户体验的重要一环,下面:本文主要介绍JS纯前端实现浏览器语音播报、朗读功能的相关资料,文中通过代码... 目录一、朗读单条文本:① 语音自选参数,按钮控制语音:② 效果图:二、朗读多条文本:① 语音有默认值:②