前言

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

webview的差异

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

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

Android

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

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

iOS

软键盘

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

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

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

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

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

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

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

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

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

所有问题产生的根本原因是: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来定位了,中间自适应撑满剩余高度,超长滚动

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

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

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)
})

这样页面展示算是正常了

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

处理滚动

我们需要禁用全局的滚动,但对一些需要滚动的区域需要放开,比如中间的列表部分

if (utils.isIOSByUA()) {
watchKeyBoard((status) => {
setTimeout(() => {
console.log(
'status',
status ? '键盘打开' : '键盘关闭',
window.innerHeight,
)
const container = document.getElementById('container')
if (status) {
container.style.height = `${
window.visualViewport.height || window.innerHeight
}px`
window.scrollTo(0, 0)
document.addEventListener('touchmove', this.stopMove, {
passive: false,
})
document.addEventListener('touchend', this.scroll)
} else {
container.style.height = `100vh`
document.removeEventListener('touchmove', this.stopMove)
document.removeEventListener('touchend', this.scroll)
}
}, 100)
})
}
stopMove(e) {
// 排除可以滚动的区域
if (['content', 'keyboard_center'].includes(e.target?.className)) return
e.preventDefault()
}
scroll() {
window.scrollTo(0, 0)
}

完整体验如下

比起它原本带来的遮挡、滚动、fixed失效等体验,现在的体验算是可以接受的(这里所有的操作我们只需要在iOS上执行即可)

如何巧妙应对iOS键盘难题?的更多相关文章

  1. iOS开发小技巧--iOS键盘 inputView 和 inputAccessoryView

    iOS键盘 inputView 和 inputAccessoryView 1.inputAccessoryView UITextFields和UITextViews有一个inputAccessoryV ...

  2. IOS - 键盘处理

    iOS 发布了很多关于屏幕上键盘的通知.下面列出了这些通知的简要解释: UIKeyboardWillShowNotification 当键盘即将要显示的时候将会发出这个通知.这个通知包含了用户信息库, ...

  3. IOS键盘弹出、隐藏

    IOS键盘 UIKeyboardFrameBeginUserInfoKey:动画开始前键盘的size UIKeyboardFrameEndUserInfoKey:动画结束后键盘的size - (voi ...

  4. 一行代码巧妙实现iOS返回button

    一行代码巧妙实现iOS返回button: self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[ ...

  5. ios键盘弹起 body的高度拉长,页面底部空白问题。ios软键盘将页面抵到上面后,关闭软键盘页面不回弹的问题。

    js 监听ios手机键盘弹起和收起的事件 /* js 监听ios手机键盘弹起和收起的事件 */ document.body.addEventListener('focusin', () => { ...

  6. iOS 键盘自适应(IQKeyboardManager)使用小结

    IQKeyboardManager Github地址 经常在开发一个应用程序,我们遇到了一个问题,iPhone的键盘上滑覆盖的UITextField / UITextView.IQKeyboardMa ...

  7. iOS键盘中英文切换键盘高度获取通知方法

    iOS键盘中英文切换键盘高度获取通知方法, 有需要的朋友可以参考下. 注册通知 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppea ...

  8. iOS 键盘遮挡输入框万能解决方案(多个输入框)

    效果图如下: 思路分析: 代码: 知识点: 问题: 效果图如下: 思路分析: 当我们有很多输入框时,有时候键盘弹出来会遮挡着输入框.我们需要获取输入框和键盘相对于最外层视图的位置来判断是否遮挡,如果遮 ...

  9. h5页面ios键盘弹出收起后页面底部留白问题

    <input placeholder="验证码" type="tel" v-model="verify" maxlength=&quo ...

  10. iOS键盘输入屏幕上移

    在iOS开法中经常会遇到键盘遮挡屏幕的事情(比如输入账号密码验证码等等),就使得原本都不大的屏幕直接占了一半甚至更多的位置,这倒无所谓,关键是挡住了下面的按钮.这样的话按钮的事件也就触发不了,最好的解 ...

随机推荐

  1. 【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM

    问题描述 在Azure的Spring Cloud服务 (官名为:Spring Apps)中,在Metrics 页面中查看 App Memory Usage 和 jvm.memory.use,发现两则在 ...

  2. 【Azure 应用服务】NodeJS项目部署在App Service For Linux环境中,部署完成后应用无法访问

    问题描述 NodeJS项目部署在App Service For Linux环境中,部署完成后应用无法访问,在启动日志中发现错误消息:ERROR - Container XXX_0 for site X ...

  3. [Linux] 快速修改hosts访问github

    sudo sed -i '/github/d' /etc/hosts sudo bash -c "curl https://gitlab.com/ineo6/hosts/-/raw/mast ...

  4. (三)Git 学习之分支操作

    一.分支简介 1.1 Git 分支初探 几乎所有的版本控制系统都会以某种形式支持分支. 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线. 假设你准备开发一个新功能,但是需要两周时 ...

  5. Obsidian 0.15.9 知识笔记 使用说明

    我感觉这个软件是一个非常好用的软件,经过初步体验. 全局搜索快捷键 Ctrl + Shift + F 打开快速切换快捷键 Ctrl + O 添加标签 #测试标签 反向链接 Obsidian支持反向链接 ...

  6. roadmap - json格式的 思维导图

    roadmap - json格式的 思维导图 前端路线图 http://www.bitcountrys.com/frontend.html https://gitee.com/ironman1987/ ...

  7. P2602 [ZJOI2010] 数字计数:数位DP

    https://www.luogu.com.cn/problem/P2602 // #include <iostream> // #include <iomanip> // # ...

  8. 视野修炼-技术周刊第73期 | AI 春联生成

    欢迎来到第 73 期的[视野修炼 - 技术周刊],下面是本期的精选内容简介 强烈推荐 AI 春联 Vue 10周年啦! 开源工具&技术资讯 2024 你应该知道的几个CSS特性 Vite 5. ...

  9. Android 圆形进度条ProgressBar实现固定进度

    原文: Android 圆形进度条ProgressBar实现固定进度-Stars-One的杂货小窝 之前遇到一个问题,发现Android里的圆形进度条无法固定一个进度,记录一下解决方法 探究 假设我们 ...

  10. FBNet/FBNetV2/FBNetV3:Facebook在NAS领域的轻量级网络探索 | 轻量级网络

    FBNet系列是完全基于NAS方法的轻量级网络系列,分析当前搜索方法的缺点,逐步增加创新性改进,FBNet结合了DNAS和资源约束,FBNetV2加入了channel和输入分辨率的搜索,FBNetV3 ...