从调用链上清晰可以看到,当WKScrollView滚动的时候,WKScrollView滚动相关回调的消息,将会发送到WKWebView内,WKWebView实例内scrollView的的回调将会调用WKContntView的刷新方法,刷新需要渲染的web内容。
当了解到WKWebView内容的刷新机制以后,就可以合理的进行猜想了。因为WKWebView作为一个普通的UIView添加在UITableViewCell的contentView上,因为项目中UITableView和WKWebView的ScrollView都是竖向滚动的,这两个手势动作将会冲突,WKWebView只是一个子View,需要通过设置内置ScrollView的滚动属性来将WKWebView的滚动功能关闭,保证父View--UITableView滚动功能的正常使用。
因为WKWebView使用过绑定内置ScrollView的滚动回调来刷新WKContentView内需要渲染的web内容的,因为WKWebView已经被设定为禁止滚动,自然不会再刷新需要渲染当初在不在可视区域的内容了。因为UITableView的滚动回调并没有和WKWebView的内的滚动是绑定关系,所以在UITableView滚动的时候,并不会触发WKWebView的刷新。这就是为什么在进入页面的时候,上面一部分内容可以正常显示,二下半部分显示白屏的原因。当然,在目前来说只是一个猜想。
前面的猜想,在经过对源代码的阅读,理论上是说得通的。现在就通过demo的代码验证。有了上面的原理,那么UITablbeView滚动的时候,触发WKWebView刷新页面即可?可知的是,WKWebView是调用_updateVisibleContentRectAfterScrollInView:方法来对WKContentView来刷新内容的。
由下图可知,而WKWebView的_updateVisibleContentRects方法实现,也只是调用了_updateVisibleContentRectAfterScrollInView:,也就是说直接调用WKWebView实例的_updateVisibleContentRects就可以刷新了。
下面,用RAC来监听一下UITableView实例的contentOffset属性,在contentOffset发生变化的时候,也就是UITableView实例滚动的时候,就去调用一下WKWebView实例的_updateVisibleContentRects方法去刷新需要渲染的内容。
@weakify(self);
[RACObserve(self.tableView, contentOffset) subscribeNext:^(id x) { @strongify(self); if ([self.webView respondsToSelector:@selector(_updateVisibleContentRects)]) {
((void(*)(id,SEL,BOOL))objc_msgSend)(self.webView,@selector(_updateVisibleContentRects),NO);
}];复制代码
在运行demo工程的时候,结果按照猜想的发生了,滚动到WKWebView下方时,原来会白屏的区域正常的渲染内容了。
上方猜想被证实了,那么说这个方案时可以行的,而且对源代码的理解并没有太大的偏差。按照原理,可以使用一下几个方法来解决白屏的问题
用KVO方法监听UITableView的contnetOffset属性,contentOffset发生变化也就是说UITableView发生滚动,调用WKWebView实例的_updateVisibleContentRects,刷新需要渲染的内容
UITableView是继承自UIScrollView的,在代码中实现UIScrollView的delegate,在delegate实现中手动调用WKWebView实例等UIScrollViewDelegate的方法,原理和第一种方法一样
使用CADisplayLink类,在CADisplayLink的回调方法里面调用WKWebView实例的_updateVisibleContentRects即可
上面三种方法的其实都是大同小异的,只是适合不同的场景。优劣也不用说了,一眼就能看出来了。
快来加入掘金 iOS 开发交流群
扫描二维码,添加小编好友,回复 ios 拉你进群
指尖上的生活
WebView
JavaScript