如何准确判断webView是否加载完成

原文地址:http://www.jianshu.com/p/897e2d82ee43

正常情况下我们把处理网页加载完毕的代码放在- (void)webViewDidFinishLoad:(UIWebView *)webView 里。但 WebViewDidFinishLoad 时网页真的加载完了吗?

官方文档并没有说明 WebViewDidFinishLoad 到底在什么时候被调用,但事实证明在某些情况下 WebViewDidFinishLoad 可能不是你想要的时机。

网页重定向

当网页重定向发生时,网址被重定向几次,WebViewDidFinishLoad 就会被调用几次。所以如果你只想在最后加载完成时调用某些代码,可以通过webView.isLoading 来判断。当 WebViewDidFinishLoad 时如果 webView.isLoading == YES 那么说明网页可能发生了重定向。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if (!webView.isLoading) {
        [self webViewDidFinishLoadCompletely];
    }
}

加载内嵌资源

除了原生方法外,网页的 readyState 属性也可以返回当前加载状态,共有5种。

1、uninitialized : 还没开始加载

2、loading : 加载中

3、loaded : 加载完成

4、interactive : 结束渲染,用户已经可以与网页进行交互。但内嵌资源还在加载中

5、 complete : 完全加载完成

WebViewDidFinishLoad 被调用时,readyState 可能处在 interactive 和 complete 两种状态。当我们需要对网页中的元素进行修改时,最好在 complete 状态进行,不然我们的修改可能被重置。例如百度登录页(http://wappass.baidu.com/passport) 在iPad上打开时,WebViewDidFinishLoad 的 readyState 就是 interactive,这时如果修改输入框里内容(账号密码自动填充),我们的修改将会在 complete 状态时被重置。

为了解决这个问题,我们可以在 WebViewDidFinishLoad 判断 readyState 的状态,如果不是 complete,则重写 window.onload 或者 document.onreadystatechange 两个方法,他们都可以准确判断内嵌资源加载完毕的时机。然后通过 JSExport 回调 Objective-C 代码(如果是 WKWebView 则通过 MessageHandler 回调)。 对 JSExport 还不熟悉可以参考 这篇博客 来简单了解 JSExport 的使用。

- (void)webViewDidStartLoad:(UIWebView *)webView {
    _jsContext = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    _jsContext[@"xfNewsContext"] = _jsExport;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if (!webView.isLoading) {
        NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
        BOOL complete = [readyState isEqualToString:@"complete"];
        if (complete) {
            [self webViewDidFinishLoadCompletely];
        } else {
            NSString *jsString =
            @"window.onload = function() {"
            @"    xfNewsContext.onload();"
            @"};"
            @"document.onreadystatechange = function () {"
            @"    if (document.readyState == \"complete\") {"
            @"        xfNewsContext.documentReadyStateComplete();"
            @"    }"
            @"};";
            [_webView stringByEvaluatingJavaScriptFromString:jsString];
        }
        NSLog(@"%@", NSStringFromSelector(_cmd));
    }
}

- (void)onload {
    [self webViewDidFinishLoadCompletely];
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)documentReadyStateComplete {
    [self webViewDidFinishLoadCompletely];
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)webViewDidFinishLoadCompletely {
    [self displayContent];
}

在普通网页加载时打印结果是:

1、documentReadyStateComplete

2、 onload

3、webViewDidFinishLoad:

而本例中打印结果则是:

1、webViewDidFinishLoad:

2、documentReadyStateComplete

3、onload

不管 webViewDidFinishLoad 在何时调用,webViewDidFinishLoadCompletely 都保证在加载完成的时候触发。 。

本文的完整代码可以在 XFNewsContentDemo 中查看。本问与上篇博客共用 Demo,所以 Demo 中有大量与本篇不相关的代码。你只需要找到上述引用代码的部分查看即可。

参考资料

[MDN] document.readyState

[w3schools] HTML DOM readyState Property

[CNBlogs]iOS判断UIWebView加载完成的方法

Last updated