一、WKWebView簡介
UIWebView自iOS2就有,WKWebView從iOS8才有,毫無疑問WKWebView將逐步取代笨重的UIWebView。通過簡單的測試即可發現UIWebView佔用過多記憶體,且記憶體峰值更是誇張。WKWebView網頁載入速度也有提升,但是並不像記憶體那樣提升那麼多。下面列舉一些其它的優勢:
1、更多的支援HTML5的特性
2、官方宣稱的高達60fps的滾動重新整理率以及內建手勢
3、Safari相同的JavaScript引擎,且允許JavaScript的Nitro庫載入並使用(UIWebView中限制);
4、將UIWebViewDelegate與UIWebView拆分成了14類與3個協議(官方文檔說明)
5、佔用更少的記憶體,在效能、穩定性、功能方面有很大提升(最直觀的體現就是載入網頁是佔用的記憶體,模擬器載入百度與開源中國網站時,WKWebView佔用23M,而UIWebView佔用85M);
另外用的比較多的,增加載入進度屬性:estimatedProgress
二、WKWebView初始化
1. 首先需要引入WebKit庫
import
2. 初始化方法分為以下兩種
1 2 3 4 |
// 預設初始化 - (instancetype)initWithFrame:(CGRect)frame; // 根據對webview的相關配置,進行初始化 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER; |
3. 載入網頁與HTML代碼的方式與UIWebView相同,代碼如下:
1 2 3 |
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@ "http://www.baidu.com" ]]]; [self.view addSubview:webView]; |
三、屬性介紹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
WKBackForwardList:之前訪問過的 web頁面的列表,可以通過後退和前進動作來訪問到。 WKBackForwardListItem: webview中後退列表裡的某一個網頁。 WKFrameInfo: 包含一個網頁的布局資訊。 WKNavigation: 包含一個網頁的載入進度資訊。 WKNavigationAction:包含可能讓網頁導航變化的資訊,用於判斷是否做出導航變化。 WKNavigationResponse:包含可能讓網頁導航變化的返回內容資訊,用於判斷是否做出導航變化。 WKPreferences: 概括一個 webview 的喜好設定。 WKProcessPool: 表示一個 web 內容載入池。 WKUserContentController: 提供使用 JavaScript post 資訊和注射 script 的方法。 WKScriptMessage: 包含網頁發出的資訊。 WKUserScript:表示可以被網頁接受的使用者指令碼。 WKWebViewConfiguration: 初始化 webview 的設定。 WKWindowFeatures: 指定載入新網頁時的視窗屬性。 WKNavigationDelegate: 提供了追蹤主視窗網頁載入過程和判斷主視窗和子視窗是否進行頁面載入新頁面的相關方法。 WKScriptMessageHandler: 提供從網頁中收訊息的回調方法。 WKUIDelegate: 提供用原生控制項顯示網頁的方法回調。 |
四、WKWebView代理
WKWebView有兩個delegate,WKUIDelegate 和 WKNavigationDelegate。WKNavigationDelegate主要處理一些跳轉、載入處理操作,WKUIDelegate主要處理JS指令碼,確認框,警告框等。因此WKNavigationDelegate更加常用。
1. WKNavigationDelegate
該代理提供的方法,可以用來追蹤載入過程(頁面開始載入、載入完成、載入失敗)、決定是否執行跳轉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#pragma mark - WKNavigationDelegate // 頁面開始載入時調用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{ } // 當內容開始返回時調用 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{ } // 頁面載入完成之後調用 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{ } // 頁面載入失敗時調用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{ } |
頁面跳轉的代理方法有三種,分為(收到跳轉與決定是否跳轉兩種)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 接收到伺服器跳轉請求之後調用 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{ } // 在收到響應後,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ NSLog(@ "%@" ,navigationResponse.response.URL.absoluteString); //允許跳轉 decisionHandler(WKNavigationResponsePolicyAllow); //不允許跳轉 //decisionHandler(WKNavigationResponsePolicyCancel); } // 在發送請求之前,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ NSLog(@ "%@" ,navigationAction.request.URL.absoluteString); //允許跳轉 decisionHandler(WKNavigationActionPolicyAllow); //不允許跳轉 //decisionHandler(WKNavigationActionPolicyCancel); } |
2. WKUIDelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#pragma mark - WKUIDelegate // 建立一個新的WebView - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{ return [[WKWebView alloc]init]; } // 輸入框 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{ completionHandler(@ "http" ); } // 確認框 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{ completionHandler(YES); } // 警告框 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ NSLog(@ "%@" ,message); completionHandler(); } |
3. WKScriptMessageHandler
這個協議中包含一個必須實現的方法,這個方法是提高App與web端互動的關鍵,它可以直接將接收到的JS指令碼轉為OC或Swift對象。(當然,在UIWebView也可以通過“曲線救國”的方式與web進行互動,著名的Cordova架構就是這種機制)
1 2 |
// 從web介面中接收到一個指令碼時調用 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message; |
4、WKWebView載入JS
1 2 3 4 5 6 7 8 9 10 |
// 圖片縮放的js代碼 NSString *js = @ "var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '張圖');" ; // 根據JS字串初始化WKUserScript對象 WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; // 根據產生的WKUserScript對象,初始化WKWebViewConfiguration WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; [config.userContentController addUserScript:script]; _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; [_webView loadHTMLString:@ "![](http://upload-images.jianshu.io/upload_images/1204112-3c87ed90109ff19f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)" baseURL:nil]; [self.view addSubview:_webView]; |
五、WKWebView使用過程中的坑
1、WKWebView下面添加自訂View
因為我們有個需求是在網頁下面在添加一個View,用來展示此連結內容的相關評論。在使用UIWebView的時候,做法非常簡單粗暴,在UIWebView的ScrollView後面添加一個自訂View,然後根據View的高度,在改變一下scrollView的contentSize屬性。以為WKWebView也可以這樣簡單粗暴的去搞一下,結果卻並不是這樣。
首先改變WKWebView的scrollView的contentSize屬性,系統會在下一次幀率重新整理的時候,再給你改變回原有的,這樣這條路就行不通了。我馬上想到了另一個辦法,改變scrollView的contentInset這個系統倒不會在變化回原來的,自以為完事大吉。後來過了兩天,發現有些頁面的部分地區的點擊事件無法響應,百思不得其解,最後想到可能是設定的contentInset對其有了影響,事實上正是如此。查來查去,最後找到了一個解決辦法是,就是當頁面載入完成時,在網頁下面拼一個空白的div,高度就是你添加的View的高度,讓網頁多出一個空白地區,自訂的View就添加在這個空白的地區上面。這樣就完美解決了此問題。具體可參考Demo所寫,核心代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
self.addView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, addViewHeight)]; self.addView.backgroundColor = [UIColor redColor]; |