摘要:着急要解决方案,直接滚到结尾复制有效代码,即可。本文记录了本人在解决这个过程的大致心路历程,可以说是摸索的过程吧。拙笔耕耘,谢谢阅读。
最近在项目中,遇到一个棘手的问题:webView装载H5页面,设置导航栏颜色和h5页面保持一致的情况下,下拉h5页面顶部会产生一个视觉断层,这对用户并不友好。于是开始了一系列的探索之路。
最初尝试的解决方式:
设置WKWebView下的scrollView的属性
webView.scrollView.bounces =false
// default NO. if YES and bounces is YES, even if content is smaller than bounds, allow drag vertically
webView.scrollView.alwaysBounceVertical = false //其实默认也是false
但简单的设置并不起作用。
于是从渲染层的效果看当前的视图层次,仔细扒拉发现当前的渲染视图层次里最上层装载h5内容的是一个WKChildScrollView ,这就tm的搞笑,是我涨了见识也是技术迭代的快啊,继续探索,下意识就是既然我可以看到这个视图,那就可以拿到这个对象进行操作。只能说幼稚坦然暴露在在未知领域的面前,且即便有着庞大的数量基数。接下来,我通过webView.scrollView.subviews[0].subviews[0].subviews[0].subviews[0]这种方式获取了5-6层的子View并且遍历,然后并没有拿到。打破了以前的这种求知路线的经验。
接下来,向另一个路线寻找解决方案,从js代码上入手,以前的工作经验告诉我,webview是可以获取到h5的上下文(JSContext),再通过“deocment.xxx.xxx...”获取H5内部控件元素。于是开启了更多偏向js层面与原生交互的学习探索,进行猛烈的炮火攻击,搜罗了市面的大多数webView与js的中文文章(少量的类似问题的英文文档),在不断加深对webview的理解中,我有一个深刻意识,可以用js解决了这个问题。
果然搜到了很多解决方案,例如解决H5页面在iOS系统中滑动回弹效果(橡皮筋效果)导致的穿透问题此类众多,或者集成一个貌似叫bounce的js库解决,就是需要前端做集成修改的方案,然而事实证明,还在路上。继续gank,终于,emmmm,上代码
document.body.addEventListener('touchmove', function (e) {
e.preventDefault();
}, {passive: false});
效果很明显,就是h5页面直接不能滑动。心里独白就是这....就离谱。标题打着已经解决的旗号,结果是医驼不保活的方案。让我想起草原上飞奔的草泥马,默默的一句“啊,呸”。
然后,继续往JSValue这种线上摸,线太长,精力有限,消耗原动力没有产出,于是放弃了(识得前景大,不因一隅陷啊)。主要是找了前端交流探讨,加上前面的经验,我下意识回到了WKWebView的本身,
然后又在一个微信iOS群里抛出了这个问题,有伙伴,给出了以下的解决方案:
let firstTempList:[UIView] = webView.scrollView.subviews
let subTempList = firstTempList.first?.subviews ?? []
subTempList.first?.backgroundColor = UIColor.grayF8()
copy+v+c之后,无法获直接取的view颜色倒是可以设置,但还是不能,又加上了
webView.scrollView.isOpaque = true
个人测试是不能通过实现设置底色的方式,消去视觉断层的问题的,但还是建议遇到类似问题尝试下。
在上述依然不能解决问题的前提下,我想这就是写博客的机会了(装了装了,就是感觉这样的问题,解决方式不应该没有以及应该说不上会很难,所以坚持继续探究的,一开始没有想到写此文章,真有文章解决,我是没心浪费精力写的)。
到这里,也是过了一些时间了,牵强附会的扯上初心吧,又回到了当初的获取WKChildScrollView的方式。不甘心的又使用了webView.scrollView.subviews[0].subviews[0].subviews[0].subviews[0]这种方式,在未知的领域面前,幼稚不分对象。但是不甘让人产生坚持心,于是我决定把subview子类链走到底,庆幸每个subviews- item并不多啊,终于,终于,看下图:(不要着急懵逼和叹服取了足足10层)
于是,终于看到晨曦的光,于是进行了下面的操作
事情进行到这,终于松了一口气。目的地是已到达,但多少还是跟完美有点距离。因为iOS的系统版本不断更新,这个解决方式只能算是解决了当前版本的当下场景的问题。而且,这样的代码作为上线代码,显然不行滴。直接强解包读取危害太大,用fisrt或last替换也是太low,而且不能兼容各种场景。于是尝试while循环,条件是subview.count > 0。 所以gank,写了几句代码,这......还不如来个递归吧。于是我的终极解决方案出来了,如下。(有一个缺点是所有的WebView的页面的下拉的弹簧效果都被禁止了,稍后给出解决方案)
func checkSubviewsIsContoinScrollView(v: UIView?) {
v?.subviews.forEach({ itemV in
if itemV.isKind(of: UIScrollView.**self**), **let** childScrollV = itemV as? UIScrollView {
childScrollV.bounces = **false**
}
self.checkSubviewsIsContoinScrollView(v: itemV)
})
}
//调用的地方以及逐渐优化
checkSubviewsIsContoinScrollView(v: webView)
// let childScrollView = webView.scrollView.subviews[0].subviews[0].subviews[0].subviews[0].subviews[0].subviews[0].subviews[1].subviews[0].subviews[0].subviews[0]
// let WKChildScrollView: AnyClass? = NSClassFromString("WKChildScrollView")
// if let WKChildScrollView = WKChildScrollView, childScrollView.isKind(of: WKChildScrollView.self) {
// if let childScrollV = childScrollView as? UIScrollView {
// childScrollV.bounces = false
// }
// }
// if childScrollView.isKind(of: UIScrollView.self) {
// if let childScrollV = childScrollView as? UIScrollView {
// childScrollV.bounces = false
// }
// }
致此,方是柳暗花明又一村啊。
缺点一: 就是所有装载的h5页面的顶部弹簧效果都被禁了,这其实不友好。
解决方案一:写一个BaseWebViewVC不实现上面的代码,然后再继承BaseWebViewVC写一个BaseNoBounceWebViewVC,实现上述代码。
解决方案二:可以直接通过获取当前导航栏是否有颜色,再结合js代码获取当前h5页面是否有背景的场景来判断是否调用上述方法。
缺点二:
本人测试的存在数据加载缓慢的情况,而这段代码并不工作(概率极小,但还需要优化)。推测是因为数据加载缓慢导致视图层次渲染延迟,在viewDidAppear中,WKChildScrollView还没有渲染生成的原因。期待大神给出这个的优化的方案。
//有尝试在此方法中调用,但是没有效果,同时进行延迟几秒的调用有生效,那基本就闭环解决了。因为测试 case量少,不能保证。
func webView(webView: WKWebView, didFinish navigation: WKNavigation!)