如何準確判斷 WebView 加載完成
正常情況下我們把處理網頁加載完畢的代碼放在 - (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種。
- uninitialized : 還沒開始加載
- loading : 加載中
- loaded : 加載完成
- interactive : 結束渲染,用戶已經可以與網頁進行交互。但內嵌資源還在加載中
- complete : 完全加載完成
WebViewDidFinishLoad 被調用時,readyState 可能處在 interactive 和 complete 兩種狀態。當我們需要對網頁中的元素進行修改時,最好在 complete 狀態進行,不然我們的修改可能被重置。例如百度登錄頁在iPad上打開時,WebViewDidFinishLoad 的 readyState 就是 interactive,這時假設想要在輸入框里自動填寫賬號密碼并修改輸入框背景為黃色,我們的修改將會在 complete 狀態時被重置。
為了解決這個問題,我們可以在 WebViewDidFinishLoad 判斷 readyState 的狀態,如果不是 complete,則重寫 window.onload 或者 document.onreadystatechange 兩個方法,他們都可以準確判斷內嵌資源加載完畢的時機。然后通過 JSExport 回調 Objective-C 代碼(如果是 WKWebView 則通過 MessageHandler 回調)
- (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];
}
在普通網頁加載時打印結果是:
- documentReadyStateComplete
- onload
- webViewDidFinishLoad:
而本例中打印結果則是:
- webViewDidFinishLoad:
- documentReadyStateComplete
- onload
不管 webViewDidFinishLoad 在何時調用,webViewDidFinishLoadCompletely 都保證在加載完成的時候觸發。
參考資料
[w3schools] HTML DOM readyState Property
[CNBlogs]iOS判斷UIWebView加載完成的方法
來自:http://www.jianshu.com/p/897e2d82ee43