记IOS下UIWebView的坑

2,772 阅读3分钟

ios在uiwebview下的坑

最近做了一个需求,需要做吸顶的效果,在安卓机器和IOS高版本系统下,能改完美实现吸顶效果,但是在低版本的IOS中,却遇到了坑,当我滚动到吸顶的位置的时候,它仍然没有吸顶,只有滚动停止的时候才吸顶。

image

google分析了原因,有两种原因会导致这种情况。

1、uiwebview下scroll事件只会在滚动停止时触发

如果你监听滚动是使用addEventListener的方式,那么很遗憾的告诉你,你中招了,通过这种方式绑定滚动事件,如标题所示,只会在滚动停止的时候才触发一次,类似防抖操作,只有在最后一次触发。

但是,如果你用window.onscroll的方式监听滚动的话,就可以在滚动的过程中触发scroll事件了,是不是很神奇?

网上还有说用touchmove来监听,也是可以在滚动的时候触发事件的,但是touchmove有两个缺点是:

  • 它触发频率低,就是需要滚动一定的距离才会触发
  • 它会惯性滚动,即我们手指离开的时候,它还会模拟惯性滚动一段距离,而这个时候,不管是scroll还是touchmove都无法触发。

然而,我改为onscroll监听滚动了,可是我还是无法及时更新吸顶元素,还是和用addEventListener来监听scroll的效果一样,在滚动停止的时候才会吸顶。这就要导出第二个会导致这个现象的原因了。

2、uiwebview下,scroll里面的所有异步操作都会被阻塞

会出现这个的原因,估计和第一个原因一样,IOS想要做一些优化,避免一些重复的操作,就和我们做函数节流一样的思想,但是出这个方案的大神,你就没想到这样一刀切的方式会让我们开发者遇到很多坑吗(借机吐槽下)?我的技术栈是用vue,而我们知道,vue是异步更新dom的,所以我在onscroll里面通过vue更新视图的操作,都会被ios阻塞,只有滚动停止后才会拿出来执行。

顺便说一句,如果你的滚动处理函数使用防抖来优化,那效果还是一样,因为防抖的实现原理是setTimeout,而setTimeout也是异步操作,都会被IOS阻塞。

解决方案

具体看大家的情况,如果是第一种原因导致的,请使用window.onscroll来监听滚动,如果是第二种原因,请避免在scroll事件中使用异步操作,而如果你是使用vue或者react这些框架,它们都会异步去更新dom。

方案一

根据是否是uiwebview,选择性地使用正常的方式和原生js操作dom更新视图

if (isUIWebView) {
    window.onscroll = function() {
        // 使用原生js操作dom
    }
} else {
    // 正常的情况,使用防抖、vue|react去操作dom
    window.addEventListener('scroll', this.scrollHandler)
}

方案二

使用第三方滚动插件模拟滚动,比如iscroll之类的。

其他坑

  • os元素设置inline-block后元素下沉的问题

因为display: inline-block成了内联,inline box有一个叫做baseline的东西,如果有的元素有多行文字,有的只有一行,就会以baseline为基准,想要更改很简单只要vertical-align: top;和vertical-align: bottom;

  • 在ios的uiwebview下,pageYOffset属性是无效的
    献上获取滚动距离的兼容性写法
function getScrollTop(el) {
  if (!el) {
    return document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
  }
  return el.scrollTop;
}