WKWebView获取高度不准确解决方案

4,780 阅读3分钟

近期随着iOS14发布,苹果要求更换UIWebView为性能更高的WKWebView。更换的过程中发现在获取WKWebView获取高度经常不准确。经过调研发现WKWebView在解析html的时候和UIWebView有些许不同。 

问题一

在WKWebView加载完成后调起下面这个代理方法,当html文件中有多张图片资源或者html长度特别长的时候返回的高度height小于html实际高度。

原因:WKWebView为了提高性能加载资源和渲染资源分开实现,当页面加载完成的时候调起一下代理方法,但此时图片资源并没有完全展示出来,因此获取的高度并不是完整的高度。也就是说以下加载html完成的代理回调方法并不可靠。

/* 页面加载完成 */- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{      [webView evaluateJavaScript:@"document.body.offsetHeight;" completionHandler:^(id Result, NSError * error) {       NSString *heightStr = [NSString stringWithFormat:@"%@",Result];       CGFloat height = heightStr.floatValue;       NSLog(@"新闻加载完成网页高度:%f",height);       webView.frame = CGRectMake(0, 0, self.view.frame.size.width, height);       self.mylable.frame =  CGRectMake(0, height, 200, 50);       self.myScrollView.contentSize = CGSizeMake(self.view.frame.size.width, height + 50);      }];}

解决方案:如果让HTML把加载完成的状态告诉客户端肯定是准确的。在 HTML DOM 中 Event 有个函数 onload 是用于一张页面或一幅图像完成加载时所执行的,我们需要监听所有的 img标签 或 body标签,然后在这个方法里发个消息给 WebKit 然后进行拦截即可。

在js中加入以下代码

获取所有的图片标签,然后在每个图片加载完之后给客户端发送消息。

<script type="text/javascript">       
 let imgArr = document.getElementsByTagName('img');    
for (var i = 0; i <= imgArr.length - 1; i++) {    
    (imgArr[i]).onload = function() {
 // 加载完成后给 webkit 发送通知           
 let height = document.body.scrollHeight;    
  window.webkit.messageHandlers.imagLoaded.postMessage(height);   
     }  
  }   
 </script>

在iOS端加入以下代码用来接收js发送的消息

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addScriptMessageHandler:self name:@"imagLoaded"];

#pragma mark - WKScriptMessageHandler- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {    if ([message.name isEqualToString:@"imagLoaded"]) {        CGFloat height = [message.body floatValue];        NSLog(@"图片加载完成网页高度:%f",height);        self.webView.frame = CGRectMake(0, 0, self.view.frame.size.width, height);        self.mylable.frame =  CGRectMake(0, height, 200, 50);
        self.myScrollView.contentSize = CGSizeMake(self.view.frame.size.width, height + 50);}

有几张图片就会调起几次代理方法,直到最后一张图片加载完成。

问题二

虽然可以准确拿到html页面资源加载完成的消息回调,但是发现页面高度依然不准确。进一步研究发现html中有以下代码实现缩放功能,怀疑是页面缩放引起

<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">

于是把 initial-scale, maximum-scale, minimum-scale的值都改成1之后看效果: 

此时获取的高度是正确的的,但是又出现了第三个问题。

问题三

页面资源没有自动适配手机屏幕,屏幕内左右宽度只展示部分内容。

进一步观察页面资源,发现文本内容基本可以适配,只有图片内容没有适配屏幕宽度。于是叫来公司前端开发的小哥一起讨论解决方案:如何适配图片资源。

前端开发小哥尝试给图片容器添加宽度width:100%。显示正常。

总结

以上问题基本可以归结为WKWebView解析html稍微有一些不同,根据不同html代码写法会产生不一样的结果,需要根据问题现象进一步调整代码即可。