參考文章:http://www.cocoachina.com/ios/20180831/24753.html
WK時蘋果在iOS8.0之后推出的控件,相比於UIWebView:
- 內存消耗少;
- 解決了網頁加載時的內存泄漏問題;
- 與HTML頁面的交互更方便;
- 總之,其性能比UIWebView好很多。
使用時,首先要添加頭文件:
#import <WebKit/WebKit.h>
簡單創建一個WKWebView:
self.iWKWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64)]; //此處協議下面會講到 self.iWKWebView.navigationDelegate = self; self.iWKWebView.UIDelegate = self; self.iWKWebView.allowsBackForwardNavigationGestures = YES; NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [self.iWKWebView loadRequest:request]; [self.view addSubview:self.iWKWebView];
基本用法和UIWebView差不多。
這里介紹幾個主要的類:
- WKWebView
-
WKWebViewConfiguration
-
WKPreferences
-
WKUserContentController
-
WKWebsiteDataStore
1. WKWebView:
常用屬性:
// 導航代理 @property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate; // UI代理 @property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate; // 頁面標題, 一般使用KVO動態獲取 @property (nullable, nonatomic, readonly, copy) NSString *title; // 頁面加載進度, 一般使用KVO動態獲取 @property (nonatomic, readonly) double estimatedProgress; // 可返回的頁面列表, 已打開過的網頁, 有點類似於navigationController的viewControllers屬性 @property (nonatomic, readonly, strong) WKBackForwardList *backForwardList; // 頁面url @property (nullable, nonatomic, readonly, copy) NSURL *URL; // 頁面是否在加載中 @property (nonatomic, readonly, getter=isLoading) BOOL loading; // 是否可返回 @property (nonatomic, readonly) BOOL canGoBack; // 是否可向前 @property (nonatomic, readonly) BOOL canGoForward; // WKWebView繼承自UIView, 所以如果想設置scrollView的一些屬性, 需要對此屬性進行配置 @property (nonatomic, readonly, strong) UIScrollView *scrollView; // 是否允許手勢左滑返回上一級, 類似導航控制的左滑返回 @property (nonatomic) BOOL allowsBackForwardNavigationGestures; //自定義UserAgent, 會覆蓋默認的值 ,iOS 9之后有效 @property (nullable, nonatomic, copy) NSString *customUserAgent
常用方法:
// 帶配置信息的初始化方法 // configuration 配置信息 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration // 加載請求 - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request; // 加載HTML - (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL; // 返回上一級 - (nullable WKNavigation *)goBack; // 前進下一級, 需要曾經打開過, 才能前進 - (nullable WKNavigation *)goForward; // 刷新頁面 - (nullable WKNavigation *)reload; // 根據緩存有效期來刷新頁面 - (nullable WKNavigation *)reloadFromOrigin; // 停止加載頁面 - (void)stopLoading; // 執行JavaScript代碼 - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
2. WKWebViewConfiguration:配置信息
可以用配置信息來初始化WKWebView.
屬性有:
//關於網頁的設置 @property (nonatomic, strong) WKPreferences *preferences; //JavaScript與原生交互的橋梁 @property (nonatomic, strong) WKUserContentController *userContentController; //提供了網站所能使用的數據類型 @property (nonatomic, strong) WKWebsiteDataStore *websiteDataStore API_AVAILABLE(macosx(10.11), ios(9.0)); //是否允許播放媒體文件 @property (nonatomic) BOOL allowsAirPlayForMediaPlayback API_AVAILABLE(macosx(10.11), ios(9.0)); //是使用h5的視頻播放器在線播放, 還是使用原生播放器全屏播放 @property (nonatomic) BOOL allowsInlineMediaPlayback; //需要用戶允許才能播放的媒體類型 @property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback API_AVAILABLE(macosx(10.12), ios(10.0));
2.1 WKPreference:
WKPreferences *preference = [[WKPreferences alloc]init]; //最小字體大小 preference.minimumFontSize = 0; //是否支持javaScript,默認YES preference.javaScriptEnabled = YES; //是否允許不經過用戶交互由javaScript自動打開窗口 preference.javaScriptCanOpenWindowsAutomatically = YES;
2.2 WKUserContentController
// 注入JavaScript與原生交互協議 // JS 端可通過 window.webkit.messageHandlers..postMessage() 發送消息 - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name; // 移除注入的協議, 在deinit方法中調用 - (void)removeScriptMessageHandlerForName:(NSString *)name; // 通過WKUserScript注入需要執行的JavaScript代碼 - (void)addUserScript:(WKUserScript *)userScript; // 移除所有注入的JavaScript代碼 - (void)removeAllUserScripts;
使用WKUserContentController注入的交互協議, 需要遵循WKScriptMessageHandler協議, 在其協議方法中獲取JavaScript端傳遞的事件和參數:
JS調用OC:
簡單理解就是:[userController addScriptMessageHandler:self name:@"JSSendToOC"];//userController是一個WKUserContentController對象,‘JSSendToOC’是方法名,
當JS端通過window.webkit.messageHandlers.JSSendToOC.postMessage()方法調用'JSSendToOC'方法時,我們可以通過下面的協議方法獲取到JS端傳過來的數據,做我們的操作。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message; WKScriptMessage包含了傳遞的協議名稱及參數, 主要從下面的屬性中獲取: // 協議名稱, 即上面的add方法傳遞的name @property (nonatomic, readonly, copy) NSString *name; // 傳遞的參數 @property (nonatomic, readonly, copy) id body;
OC調用JS:
NSString *js = @"callJsFunction('hahaha')"; [self.webView evaluateJavaScript:js completionHandler:^(id _Nullable response, NSError * _Nullable error) { NSLog(@"response:%@..error:%@",response,error); }];
這里是調用了JS的‘callJsFunction’方法,這個方法名是隨便起的。
2.3 WKWebsiteDataStore
WKWebsiteDataStore 提供了網站所能使用的數據類型,包括 cookies,硬盤緩存,內存緩存活在一些WebSQL的數據持久化和本地持久化。可通過 WKWebViewConfiguration類的屬性 websiteDataStore 進行相關的設置。WKWebsiteDataStore相關的API也比較簡單:
// 默認的data store + (WKWebsiteDataStore *)defaultDataStore; // 如果為webView設置了這個data Store,則不會有數據緩存被寫入文件 // 當需要實現隱私瀏覽的時候,可使用這個 + (WKWebsiteDataStore *)nonPersistentDataStore; // 是否是可緩存數據的,只讀 @property (nonatomic, readonly, getter=isPersistent) BOOL persistent; // 獲取所有可使用的數據類型 + (NSSet<NSString *> *)allWebsiteDataTypes; // 查找指定類型的緩存數據 // 回調的值是WKWebsiteDataRecord的集合 - (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler; // 刪除指定的紀錄 // 這里的參數是通過上面的方法查找到的WKWebsiteDataRecord實例獲取的 - (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler; // 刪除某時間后修改的某類型的數據 - (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler; // 保存的HTTP cookies @property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore
dataTypes:
// 硬盤緩存 WKWebsiteDataTypeDiskCache, // HTML離線web應用程序緩存 WKWebsiteDataTypeOfflineWebApplicationCache, // 內存緩存 WKWebsiteDataTypeMemoryCache, // 本地緩存 WKWebsiteDataTypeLocalStorage, // cookies WKWebsiteDataTypeCookies, // HTML會話存儲 WKWebsiteDataTypeSessionStorage, // IndexedDB 數據庫 WKWebsiteDataTypeIndexedDBDatabases, // WebSQL 數據庫 WKWebsiteDataTypeWebSQLDatabases
dataRecord:
// 展示名稱, 通常是域名 @property (nonatomic, readonly, copy) NSString *displayName; // 包含的數據類型 @property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;
關於此類的簡單使用:
1.刪除指定時間的所有類型數據
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{ // Done NSLog(@"釋放"); }];
2.查找刪除
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) { for (WKWebsiteDataRecord *record in records) { [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{ // done }]; } }];
3.查找刪除特定的內容
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) { for (WKWebsiteDataRecord *record in records) { if ([record.displayName isEqualToString:@"baidu"]) { [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{ // done }]; } } }];
WKNavigationDelegate:

#pragma mark - WKNavigationDelegate //請求加載之前,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ NSLog(@"加載前允許跳轉"); decisionHandler(WKNavigationActionPolicyAllow); } //開始加載時調用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ NSLog(@"開始加載"); } //收到響應開始加載后,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ NSLog(@"收到響應后允許跳轉"); decisionHandler(WKNavigationResponsePolicyAllow); } //內容開始返回時調用 - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{ NSLog(@"開始返回內容"); } //加載完成時調用 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{ NSLog(@"加載完成"); self.title = webView.title; } //加載失敗調用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{ NSLog(@"加載失敗"); } //收到服務器重定向請求后調用 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ NSLog(@"服務器重定向"); } //當main frame最后下載數據失敗時,會回調 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{ NSLog(@"返回內容發生錯誤"); } //用於授權驗證的API,與AFN、UIWebView的授權驗證API是一樣的 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ completionHandler(NSURLSessionAuthChallengePerformDefaultHandling,nil); } //當web content處理完成時,會回調 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)){ NSLog(@"WebContent完成"); }
這里放一個完整的WKWebView例子,僅供參考:
//初始化WKPreferences,並設置相關屬性 WKPreferences *preference = [[WKPreferences alloc]init]; //初始化WKUserContentController,並設置相關屬性 WKUserContentController *userController = [[WKUserContentController alloc]init]; // 添加在js中操作的對象名稱,通過該對象來向web view發送消息 // JS 端可通過 window.webkit.messageHandlers..postMessage() 發送消息 // <script type="text/javascript"> // function clickBtn(){ // var dict = {"name":"tom","age":"20"}; // window.webkit.messageHandlers.JSSendToOC.postMessage(dict); // } // </script> [userController addScriptMessageHandler:self name:@"JSSendToOC"]; //初始化WKWebsiteDataStore,並設置相關屬性 WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; // 如果為webView設置了這個data Store,則不會有數據緩存被寫入文件 // 當需要實現隱私瀏覽的時候,可使用這個 // WKWebsiteDataStore *dataStore = [WKWebsiteDataStore nonPersistentDataStore]; //配置信息 WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init]; configuration.preferences = preference; configuration.userContentController = userController; configuration.websiteDataStore = dataStore; self.iWKWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64) configuration:configuration]; self.iWKWebView.navigationDelegate = self; self.iWKWebView.UIDelegate = self; self.iWKWebView.allowsBackForwardNavigationGestures = YES; NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [self.iWKWebView loadRequest:request]; [self.view addSubview:self.iWKWebView];
再加一個知識點:WKWebView加載的時候添加一個自定義的進度條。
此時我們需要獲取到webview加載的進度數值。
這里可以通過添加監聽來獲取。
[self.iWKWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
estimatedProgress是WKWebView的一個屬性。
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ if ([keyPath isEqualToString:@"estimatedProgress"] && object==self.iWKWebView) { //獲取到webview的進度數值,加載自定義的進度條 //self.iWKWebView.estimatedProgress } }