首页 > 其他分享 >如何巧妙应对iOS键盘难题?

如何巧妙应对iOS键盘难题?

时间:2024-04-11 11:22:06浏览次数:28  
标签:iOS 巧妙 键盘 window webview document 页面

前言

写过移动端的同学或多或少都遇到过软键盘带来的各种各样的问题,最典型的就是输入框被软键盘遮挡、fixed元素失效等问题,并且这些问题在iOS上的表现让人难以接受。

webview的差异

在移动端上,我们的H5页面一般是运行在宿主APP提供的webview中,简单点理解,你其实可以把它当作浏览器,就是用来展现页面内容的。目前移动端主流系统分为AndroidiOS,然而两者提供的webview容器也存在着诸多差异,今天我们就只探讨两者软键盘带来的影响。

首先,我们先来写个简单的页面布局:头部fixed+中间自适应+底部fixed

k2.png

Android

事实上Android的表现并不会有太大问题,它只不过是在键盘弹起来之后把webview的高度减小了,变成了:原来的webview高度减去键盘的高度

k2.png

这样的表现正是我们期待的,完全没有影响整个页面的布局

iOS

软键盘

在iOS 8.2 之后,iOS 唯一指定浏览器内核、Webkit 鼻祖 Safari 将 fixed 元素的布局基准区域从键盘上方的可见区域改成了键盘背后的整个视窗,也就是说此时的webview高度并不会发生变化,键盘是直接盖在webview上方的。

这样是为了在键盘弹起来之后,不用重新渲染页面,他们是方便了,但遭殃的是我们前端开发人员...

比如上面这个页面,我们看看iOS的表现是怎样的:

k3.png

可以看到,iOS为了不让webview压缩,并且为了不让软键盘遮挡输入框,他们自作聪明地把webview整体往上移动,最大移动距离为软键盘的高度。

这样就导致我们的头部以及页面上半部分内容移动到了可视区之外,这个表现是难以接受的,至少头部应该还要在可视区。(这就会让我们误以为fixed失效,实际上它相对于webview的位置并没有变,只不过是webview发生了移动)

这个移动似乎没有逻辑,不信大家可以试试把输入框放到页面的各个位置,我发现只有输入框在最顶部,webview才不会发生上移,其它位置都或多或少的会产生移动。

还有一个问题就是,此时的webview是可以滑动的,那么就会出现有用户会将输入框滑动到键盘下方,想想这个体验也是难以接受的...

k3-1.gif

并且你会发现,在页面的上方与下方都多出了一个不论是 Viewport 还是 VisualViewport 都无法到达的白色衬底区域,我们可以尝试把页面所有元素背景都改成黑色再来看,会更加明显

k3-2.gif

看到这些奇奇怪怪的问题你心里作何感想??

所有问题产生的根本原因是:iOS为了不用在键盘弹起之后重新渲染页面,他们并没有去压缩webview容器的高度,而是对webview整体进行平移处理

软键盘监听

对于Android,我们通常可以通过监听resize事件来实现,但对于iOS,我们从上面了解到键盘弹起,iOS的webview高度并不会发生变化,所以也就触发不了resize事件。

在iOS中,可以通过focusin & focusout事件来进行监听

export const watchKeyBoard = (callback: (isShow: boolean) => void) => {
  //  IOS
  if (isIOSByUA()) {
    document.body.addEventListener('focusin', () => {
      //软键盘弹出的事件处理
      callback(true)
    })
    document.body.addEventListener('focusout', () => {
      //软键盘收起的事件处理
      callback(false)
    })
  } else {
    //  Android
    const originalHeight =
      document.documentElement.clientHeight || document.body.clientHeight
    window.addEventListener('resize', () => {
      const resizeHeight =
        document.documentElement.clientHeight || document.body.clientHeight
      if (resizeHeight - 0 < originalHeight - 0) {
        // 键盘弹起事件
        callback(true)
      } else {
        // 键盘收起事件
        callback(false)
      }
    })
  }
}

解决方案

了解完产生问题的原因,我们就可以来尝试着解决问题,但想要纯前端去解决这个问题,或多或少都会存在一些体验问题,也许你可以去推动你们的客户端同学来协助处理这个问题,只要让iOS的webview在键盘弹起时的表现与Android一致,就不会存在这些奇怪的问题了,但似乎他们处理起来也非常棘手...

模仿Android的处理

虽然我们改不了webview的高度,但我们可以改我们布局的高度,我们只需要将页面高度改为页面可视区的高度即可,如果页面内容有滚动交互的话,需要额外处理,要与webview的滚动隔离开。

VisualViewport

先来了解下这个API,它可以用来获取对应 window 的视觉视口

  • VisualViewport.offsetLeft :返回视觉视口的左边框到布局视口的左边框的 CSS 像素距离。
  • VisualViewport.offsetTop:返回视觉视口的上边框到布局视口的上边框的 CSS 像素距离。
  • VisualViewport.pageLeft:返回相对于初始的 viewport 属性的 X 轴坐标所对应的 CSS 像素数。
  • VisualViewport.pageTop:返回相对于初始的 viewport 属性的 Y 轴坐标所对应的 CSS 像素数。
  • VisualViewport.width:返回视觉视口的宽度所对应的 CSS 像素数。
  • VisualViewport.height:返回视觉视口的高度所对应的 CSS 像素数。
  • VisualViewport.scale:返回当前视觉视口所应用的缩放比例。

这里我们需要的就是这个VisualViewport.height,用来获取可视区的高度。

但需要注意的是,这个API最低只支持iOS13,ios13以下的使用window.innerHeight兜底

页面布局

整体布局采用flex布局,头部和底部也就不需要fixed来定位了,中间自适应撑满剩余高度,超长滚动

k4.png

键盘打开计算高度重新布局

我们需要在键盘弹起后,计算可视区的高度,并将最外层容器高度赋值为可视区高度

watchKeyBoard((status) => {
  setTimeout(() => {
    console.log(
      'status',
      status ? '键盘打开' : '键盘关闭',
    )
    const container = document.getElementById('container')
    if (status) {
      container.style.height = `${
      window.visualViewport.height || window.innerHeight
    }px`
      window.scrollTo(0, 0)
    } else {
      container.style.height = `100vh`
      document.removeEventListener('touchmove', this.stopMove)
    }
  }, 100)
})

这样页面展示算是正常了

k5.png

但是随之而来的是滚动问题

标签:iOS,巧妙,键盘,window,webview,document,页面
From: https://www.cnblogs.com/songyao666/p/18128484

相关文章

  • Java登陆第四十二天——Axios拦截器
    如果想在axios发送HTTP请求之前。或者是接收响应之前做一些额外的工作,可以通过拦截器完成。Axios拦截器分为请求拦截器,响应拦截器。分别在请求或响应时生效。一图了解Axios拦截器提供了两种文本函数:名字太长,直接看语法语法格式如下://请求拦截器,f1对应请求发送成功函数,f2......
  • WDS+MDT网络启动自动部署windows(三)UEFI & BIOS 双PXE引导
    简介:我们可以通过调整启动文件来兼容不同的硬件(UEFI&BIOS),能否不手动调整呢?自动调整也是可以的。本来是是想将DHCP放在H3C5500上的,但是咨询过H3C的售前顾问后,没有任何一个型号支持这个功能,前面已经折腾过自动识别客户端类型,发送不同的启动文件了。为了更好的完成这个系列文章......
  • IOS开发Archives打包后构建版本发布到TestFlight全流程
    前言:构建版本之前一定要先配置好项目icons,不然会报错。1.选择需要构建的包之后,点击右侧的DistributeApp按钮:2.Selectamethodofdistribution界面,选择AppStoreConnect(要发布到TestFlight需要选这个)3.Selectadestination——选择Upload(如果选择Export,则需要自己用......
  • Java登陆第四十一天——Axios
    Vue推荐使用axios来完成ajax请求。axios中文文档AxiosAxios是一款基于Promise,用于发送HTTP请求和处理HTTP响应的工具库。内部也是使用原生的ajax对象发送HTTP请求。所以,在使用它前需要导入依赖。npminstallaxios提供了一个函数:axios()语法格式如下://查看源码,默认......
  • 面试常问问题——web端测试、安卓测试、ios测试的区别是什么?
    web端需要考虑:1、浏览器种类的兼容2、浏览器版本的兼容3、电脑和显示器的显示4、缩放窗口大小的显示5、响应时间 app测试需要考虑:1、不同的屏幕分辨率2、不同品牌的设备3、不同的系统4、不同的安卓版本5、安装卸载......
  • 在Windows电脑上上传iOS应用至App Store
     引言......
  • VueJs使用axios上传文件
    html<inputtype="file"id="fileUploadEle"ref="fileUploadEle"style="display:none;"accept=".png,.jpg"@change="selectFile"/><labelfor="fil......
  • 项目三简易计算器 任务3-1矩阵键盘测试
    任务描述:单片机连接8位共阳极数码管和4*4矩阵键盘,编写键盘测试程序,按下任意按键,显示8个相同数字。线与线之间是立体的,交叉但不相连,没有结点,相当于立交桥。每一个按键相当于上桥下桥口,连接两条路。起到短路开关作用,按下将对应的行列连在一起。 悬空为1,1与0,相连时,1会变0,即......
  • axios配置全局过滤器
    importaxiosfrom'axios'constservice=axios.create({baseURL:'/api',//注意!!这里是全局统一加上了'/api'前缀,也就是说所有接口都会加上'/api'前缀在,页面里面写接口的时候就不要加'/api'了,否则会出现2个'/api',类似'/api/api/user'......
  • iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)
     引言在iOS开发中,将IPA文件上传到苹果开发者中心是一个重要的步骤。通常情况下,我们需要使用Mac电脑上的Xcode或ApplicationLoader工具来完成这个任务。然而,如果你没有Mac电脑,也没有关系,本文将介绍一些无需Mac电脑的方法来实现IPA文件的上传。  1.使用......