正常情況下我們把處理網(wǎng)頁(yè)加載完畢的代碼放在 - (void)webViewDidFinishLoad:(UIWebView *)webView
里权逗。但 WebViewDidFinishLoad 時(shí)網(wǎng)頁(yè)真的加載完了嗎荣茫?
官方文檔并沒(méi)有說(shuō)明 WebViewDidFinishLoad 到底在什么時(shí)候被調(diào)用,但事實(shí)證明在某些情況下 WebViewDidFinishLoad 可能不是你想要的時(shí)機(jī)。
網(wǎng)頁(yè)重定向
當(dāng)網(wǎng)頁(yè)重定向發(fā)生時(shí)头滔,網(wǎng)址被重定向幾次嗅回,WebViewDidFinishLoad 就會(huì)被調(diào)用幾次。所以如果你只想在最后加載完成時(shí)調(diào)用某些代碼肝箱,可以通過(guò)webView.isLoading
來(lái)判斷哄褒。當(dāng) WebViewDidFinishLoad 時(shí)如果 webView.isLoading == YES 那么說(shuō)明網(wǎng)頁(yè)可能發(fā)生了重定向。
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (!webView.isLoading) {
[self webViewDidFinishLoadCompletely];
}
}
加載內(nèi)嵌資源
除了原生方法外煌张,網(wǎng)頁(yè)的 readyState 屬性也可以返回當(dāng)前加載狀態(tài)呐赡,共有5種。
- uninitialized : 還沒(méi)開(kāi)始加載
- loading : 加載中
- loaded : 加載完成
- interactive : 結(jié)束渲染唱矛,用戶(hù)已經(jīng)可以與網(wǎng)頁(yè)進(jìn)行交互罚舱。但內(nèi)嵌資源還在加載中
- complete : 完全加載完成
WebViewDidFinishLoad 被調(diào)用時(shí),readyState 可能處在 interactive 和 complete 兩種狀態(tài)绎谦。當(dāng)我們需要對(duì)網(wǎng)頁(yè)中的元素進(jìn)行修改時(shí)管闷,最好在 complete 狀態(tài)進(jìn)行,不然我們的修改可能被重置窃肠。例如百度登錄頁(yè)(http://wappass.baidu.com/passport) 在iPad上打開(kāi)時(shí)包个,WebViewDidFinishLoad 的 readyState 就是 interactive,這時(shí)如果修改輸入框里內(nèi)容(賬號(hào)密碼自動(dòng)填充)冤留,我們的修改將會(huì)在 complete 狀態(tài)時(shí)被重置碧囊。
為了解決這個(gè)問(wèn)題,我們可以在 WebViewDidFinishLoad 判斷 readyState 的狀態(tài)纤怒,如果不是 complete糯而,則重寫(xiě) window.onload 或者 document.onreadystatechange 兩個(gè)方法,他們都可以準(zhǔn)確判斷內(nèi)嵌資源加載完畢的時(shí)機(jī)泊窘。然后通過(guò) JSExport 回調(diào) Objective-C 代碼(如果是 WKWebView 則通過(guò) MessageHandler 回調(diào))熄驼。對(duì) JSExport 還不熟悉可以參考 這篇博客 來(lái)簡(jiǎn)單了解 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];
}
在普通網(wǎng)頁(yè)加載時(shí)打印結(jié)果是:
- documentReadyStateComplete
- onload
- webViewDidFinishLoad:
而本例中打印結(jié)果則是:
- webViewDidFinishLoad:
- documentReadyStateComplete
- onload
不管 webViewDidFinishLoad 在何時(shí)調(diào)用烘豹,webViewDidFinishLoadCompletely 都保證在加載完成的時(shí)候觸發(fā)瓜贾。
本文的完整代碼可以在 XFNewsContentDemo 中查看。本問(wèn)與上篇博客共用 Demo携悯,所以 Demo 中有大量與本篇不相關(guān)的代碼祭芦。你只需要找到上述引用代碼的部分查看即可。
參考資料
[MDN] document.readyState
[w3schools] HTML DOM readyState Property
[CNBlogs]iOS判斷UIWebView加載完成的方法