微信小程序解决ios页面上推问题

相信大家写原生小程序都遇到过一个问题,当输入框聚焦键盘弹起时,页面会自动上推,使得输入框刚好位于键盘之上,在安卓中推动的只是内容,但在ios中,推动的是整个页面,导致导航栏被推出屏幕外,如下:

在这里插入图片描述
针对这个问题,目前的解决方案是将自动上推改成手动上推,让我们自己来控制页面内容的滚动。

一、方案一

1.取消自动上推

微信小程序中的input和textarea都有一个属性adjust-position,将其改为false
在这里插入图片描述

2.添加类名或者id

我们给每个输入框或者需要定位到键盘之上的元素添加唯一类名或者id,另外,我们还要给input或textarea添加自定义属性,值也为同一个类名或者id。
在这里插入图片描述
如上图,我期望键盘弹起能刚好将整个输入栏顶在键盘之上,所以我选择给这一栏加上唯一类名,里面的input自定义属性值为该输入栏的唯一类名,这样做事为了当我触发键盘事件时,能拿到当前输入栏的类名,获取该元素的坐标信息。

3、绑定键盘事件

input和textarea,微信小程序官方提供了键盘弹起的事件
在这里插入图片描述
这个方法里面的逻辑是本次的重点,主要是计算手动推动距离,先看代码:


 data:{
  isIos:wx.getSystemInfoSync().platform === 'ios',
 },

// 监听页面软键盘弹起手动推动页面
bindkeyboardheightchange(e) {
	// 只对ios 处理
	if(!this.data.isIos === 'ios'){
		return
	}

  // 键盘高度
  const height = e.detail.height;
  const className = e.target.dataset.class;
  if (height === 0) {
    this.scrollToInput(0);
    return;
  }
  try {
    this.createSelectorQuery()
      .select(`.${className}`)
      .boundingClientRect((res) => {
       // 可使用窗口高度
        const windowHeight = wx.getSystemInfoSync().windowHeight;
        // 除去键盘的剩余高度
        let restHeight = windowHeight - height;
        // 元素左下角坐标
        let bottom = res.bottom;
        // 只有当元素被软键盘覆盖的时候才上推页面
        if (bottom <= restHeight) return;
        // 现阶段需要滚动的大小
        let scrollTop = bottom - restHeight;
        this.scrollToInput(height, scrollTop);
      })
      .exec();
  } catch (error) {}
}

// 获取页面滚动条位置
getScrollOffset() {
  return new Promise((resolve) => {
    try {
      wx.createSelectorQuery()
        .selectViewport()
        .scrollOffset((res) => {
          resolve(res.scrollTop);
        })
        .exec();
    } catch (error) {
      resolve(0);
    }
  });
}

// 监听页面软键盘弹起手动推动页面
scrollToInput(keyboardHeight, scrollTop) {
  this.setData({
    keyboardHeight,
  });
  if (scrollTop) {
    try {
      this.getScrollOffset().then((lastScrollTop) => {
        wx.pageScrollTo({
          // 如果已经存在滚动,在此基础上继续滚
          scrollTop: lastScrollTop ? lastScrollTop + scrollTop : scrollTop,
          duration: 300,
        });
      });
    } catch (error) {}
  }
}

这里涉及到几个值,参见下图:
在这里插入图片描述

注意:这里的页面使用的是原生导航栏,若使用的是自定义导航栏,那么B/D/E/H都会再加上G区域,E/H在官方文档有说到,是元素基于显示区域的坐标位置。
1、键盘弹起后,获取到键盘的高度C,用显示区域B减去键盘区域C就是我们可使用的区域D 2、获取输入栏底部距离显示区域的坐标,如E/H
3、若输入栏底部坐标小于可使用区域D,如H,则说明当键盘弹起时,该输入栏不会被键盘遮挡,不需要推动
4、反之,若大于D,如E,则说明键盘弹起时,输入栏会被键盘遮挡,这个时候就需要页面上推至输入栏完全展示出来
5、针对4,将E减去D,得到一个差值F,这就是当前元素距离完全展示还需要滚动的距离
6、页面实际滚动距离应该为F加上页面之前已经有的滚动距离,所以在滚动之前,需要再获取一次当前页面的滚动距离
7、这里可能会存在一个问题,页面的高度不够,无法滚动这么长的距离,因此,当键盘弹起时,这里需要给页面增加高度,这里直接是增加的键盘高度

重点:包含Input的view 加入class=“form-input-1" 或者直接给input加入 input标签则需加入adjust-position="{{isIos?false:true}}" bindkeyboardheightchange="bindkeyboardheightchange" data-class="form-input-1"

// 这里的view 指的是最外层的view 这里的180rpx 是我对ios 安全区的高度
<view style="height:calc(100% + {{keyboardHeight? (keyboardHeight + 'px'):'100%'}});padding-bottom:calc(180rpx +  {{keyboardHeight?  120 + 'px':0}}) "">

	<view></view>
	............
 	<input bindfocus="stationNameFocus" type="text" placeholder="请输入" placeholder-style="color:#C9C9C9" value="		{{params.formData.WIDGET_1689834780127}}" bindblur="changeParam" data-num="11" style="width: 100%;" bind:input="hxInput" 	adjust-position="{{isIos?false:true}}" bindkeyboardheightchange="changeKeyHeight" data-class="form-input-7" />

</view>

到这里,我们就已经实现了页面自动上推的功能了。另外,这里可以根据实际情况来做个判断,一般情况下
最终实现效果如下:,安卓我们可以直接使用原生的推动,即adjust-position为true,ios使用手动上推。
在这里插入图片描述

二、方案二

有些手机或者版本过低,监听不到键盘事件,可以使用聚焦事件和失焦事件代替,事件对象中也返回了键盘的高度。
在这里插入图片描述

// 监听页面软键盘弹起手动推动页面
bindfocus(e) {
  const height = e.detail.height;
  const className = e.target.dataset.class;
  if (height === 0) {
    this.scrollToInput(0);
    return;
  }
  try {
    this.createSelectorQuery()
      .select(`.${className}`)
      .boundingClientRect((res) => {
        ......
        this.scrollToInput(height, scrollTop);
      })
      .exec();
  } catch (error) {}
}

bindblur(e) {
    this.scrollToInput(0);
  }

对比两种方案
1.,方案一的推动是及时的,方案二有一点点延迟

三、疑难杂症

1、问题:在方案一中,如果textarea展示了原生完成,在点击完成时,或者失焦键盘落下事件未监听到
解决:配合bindblur或者bindconfirm,将keyboardHeight设为0

// 监听页面软键盘弹起手动推动页面
scrollToInput(keyboardHeight, scrollTop) {
  this.setData({
    keyboardHeight,
  });
  if (scrollTop) {
    ......
  }
}

  bindblur(e) {

    this.scrollToInput(0);

  }

  bindconfirm() {

    this.scrollToInput(0);

  }

2、问题:获取元素的坐标时,会默认保留全部小数,我们都知道,js在计算的时候会存在精度问题,有可能会滚动错误

解决:获取到元素坐标后,最好只保留两位小数,计算时注意处理精度

3、问题:当页面同时有input和textarea时,若只给textarea绑定键盘事件,input会触发该textarea的键盘事件
4、问题:bindkeyboardheightchange会触发多次,某些特殊情况中,每次的高度获取不一致,导致滚动多次

解决1:使用方案二

解决2:打印每次获取的高度,看哪一次是对的,使用节流或者防抖获取正确的数据

5、问题:当页面同时有input和textarea,并且textarea添加了原生的完成那栏,先点击textarea触发键盘事件,再点击input触发键盘事件,input获取到的键盘高度是有完成那栏的,导致页面上推距离不准

解决:不要使用原生的完成,自定义一个完成,键盘弹起时将他使用动画移动到键盘之上,这个时候记得在计算D区域的时候,要减去自定义完成栏的高度

如果非要用原生的完成,可以参考一下这个方法:使用方案一,bindkeyboardheightchange事件添加防抖,获取到真实的键盘高度,页面中添加两个变量,一个是input的高度,一个是textarea的高度,当输入框聚焦获取到键盘高度时,判断当前类型的高度是否有值,没有就赋值,有就用之前的值

const height = e.detail.height;
const type = e.target.dataset.type;
this.data[type] = this.data[type] || height;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值