简析在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

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果: 解密后的数据就是正常数据: 后端:使用的是spring-cloud框架,在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> 编写一个AES加密