前言
最近項目中的UIWebView被替換為了WKWebView,因此來總結一下。 示例Demo:WKWebView的使用 本文將從以下幾方面介紹WKWebView:
- 1、WKWebView涉及的一些類
- 2、WKWebView涉及的代理方法
- 3、網頁內容加載進度條和title的實現
- 4、JS和OC的交互
- 5、本地HTML文件的實現
一、WKWebView涉及的一些類
- WKWebView:網頁的渲染與展示
注意: #import <WebKit/WebKit.h> //初始化 _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config]; // UI代理 _webView.UIDelegate = self; // 導航代理 _webView.navigationDelegate = self; // 是否允許手勢左滑返回上一級, 類似導航控制的左滑返回 _webView.allowsBackForwardNavigationGestures = YES; //可返回的頁面列表, 存儲已打開過的網頁 WKBackForwardList * backForwardList = [_webView backForwardList]; // NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.chinadaily.com.cn"]]; // [request addValue:[self readCurrentCookieWithDomain:@"http://www.chinadaily.com.cn"] forHTTPHeaderField:@"Cookie"]; // [_webView loadRequest:request]; //頁面后退 [_webView goBack]; //頁面前進 [_webView goForward]; //刷新當前頁面 [_webView reload]; NSString *path = [[NSBundle mainBundle] pathForResource:@"JStoOC.html" ofType:nil]; NSString *htmlString = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; //加載本地html文件 [_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]]; 復制代碼
- WKWebViewConfiguration:為添加WKWebView配置信息
//創建網頁配置對象
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 創建設置對象
WKPreferences *preference = [[WKPreferences alloc]init];
//最小字體大小 當將javaScriptEnabled屬性設置為NO時,可以看到明顯的效果
preference.minimumFontSize = 0;
//設置是否支持javaScript 默認是支持的
preference.javaScriptEnabled = YES;
// 在iOS上默認為NO,表示是否允許不經過用戶交互由javaScript自動打開窗口
preference.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preference;
// 是使用h5的視頻播放器在線播放, 還是使用原生播放器全屏播放
config.allowsInlineMediaPlayback = YES;
//設置視頻是否需要用戶手動播放 設置為NO則會允許自動播放
config.requiresUserActionForMediaPlayback = YES;
//設置是否允許畫中畫技術 在特定設備上有效
config.allowsPictureInPictureMediaPlayback = YES;
//設置請求的User-Agent信息中應用程序名稱 iOS9后可用
config.applicationNameForUserAgent = @"ChinaDailyForiPad"; //自定義的WKScriptMessageHandler 是為了解決內存不釋放的問題 WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self]; //這個類主要用來做native與JavaScript的交互管理 WKUserContentController * wkUController = [[WKUserContentController alloc] init]; //注冊一個name為jsToOcNoPrams的js方法 [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcNoPrams"]; [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"]; config.userContentController = wkUController; 復制代碼
- WKUserScript:用於進行JavaScript注入
//以下代碼適配文本大小,由UIWebView換為WKWebView后,會發現字體小了很多,這應該是WKWebView與html的兼容問題,解決辦法是修改原網頁,要么我們手動注入JS
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"; //用於進行JavaScript注入 WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; [config.userContentController addUserScript:wkUScript]; 復制代碼
- WKUserContentController:這個類主要用來做native與JavaScript的交互管理
//這個類主要用來做native與JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//注冊一個name為jsToOcNoPrams的js方法,設置處理接收JS方法的代理
[wkUController addScriptMessageHandler:self name:@"jsToOcNoPrams"]; [wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"]; config.userContentController = wkUController; //用完記得移除 //移除注冊的js方法 [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"]; [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"]; 復制代碼
- WKScriptMessageHandler:這個協議類專門用來處理監聽JavaScript方法從而調用原生OC方法,和WKUserContentController搭配使用。
注意:遵守WKScriptMessageHandler協議,代理是由WKUserContentControl設置
//通過接收JS傳出消息的name進行捕捉的回調方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo); //用message.body獲得JS傳出的參數體 NSDictionary * parameter = message.body; //JS調用OC if([message.name isEqualToString:@"jsToOcNoPrams"]){ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js調用到了oc" message:@"不帶參數" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }])]; [self presentViewController:alertController animated:YES completion:nil]; }else if([message.name isEqualToString:@"jsToOcWithPrams"]){ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js調用到了oc" message:parameter[@"params"] preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }])]; [self presentViewController:alertController animated:YES completion:nil]; } } 復制代碼
二、WKWebView涉及的代理方法
- WKNavigationDelegate :主要處理一些跳轉、加載處理操作
// 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
// 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO]; } // 當內容開始返回時調用 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation { } // 頁面加載完成之后調用 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [self getCookie]; } //提交發生錯誤時調用 - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { [self.progressView setProgress:0.0f animated:NO]; } // 接收到服務器跳轉請求即服務重定向時之后調用 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation { } // 根據WebView對於即將跳轉的HTTP請求頭信息和相關信息來決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSString * urlStr = navigationAction.request.URL.absoluteString; NSLog(@"發送跳轉請求:%@",urlStr); //自己定義的協議頭 NSString *htmlHeadString = @"github://"; if([urlStr hasPrefix:htmlHeadString]){ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通過截取URL調用OC" message:@"你想前往我的Github主頁?" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }])]; [alertController addAction:([UIAlertAction actionWithTitle:@"打開" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]]; [[UIApplication sharedApplication] openURL:url]; }])]; [self presentViewController:alertController animated:YES completion:nil]; decisionHandler(WKNavigationActionPolicyCancel); }else{ decisionHandler(WKNavigationActionPolicyAllow); } } // 根據客戶端受到的服務器響應頭以及response相關信息來決定是否可以跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ NSString * urlStr = navigationResponse.response.URL.absoluteString; NSLog(@"當前跳轉地址:%@",urlStr); //允許跳轉 decisionHandler(WKNavigationResponsePolicyAllow); //不允許跳轉 //decisionHandler(WKNavigationResponsePolicyCancel); } //需要響應身份驗證時調用 同樣在block中需要傳入用戶身份憑證 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ //用戶身份信息 NSURLCredential * newCred = [[NSURLCredential alloc] initWithUser:@"user123" password:@"123" persistence:NSURLCredentialPersistenceNone]; //為 challenge 的發送方提供 credential [challenge.sender useCredential:newCred forAuthenticationChallenge:challenge]; completionHandler(NSURLSessionAuthChallengeUseCredential,newCred); } //進程被終止時調用 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{ } 復制代碼
- WKUIDelegate :主要處理JS腳本,確認框,警告框等
/**
* web界面中有彈出警告框時調用
*
* @param webView 實現該代理的webview
* @param message 警告框中的內容
* @param completionHandler 警告框消失調用
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的彈出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }])]; [self presentViewController:alertController animated:YES completion:nil]; } // 確認框 //JavaScript調用confirm方法后回調的方法 confirm是js中的確定框,需要在block中把用戶選擇的情況傳遞進去 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { completionHandler(NO); }])]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(YES); }])]; [self presentViewController:alertController animated:YES completion:nil]; } // 輸入框 //JavaScript調用prompt方法后回調的方法 prompt是js中的輸入框 需要在block中把用戶輸入的信息傳入 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { textField.text = defaultText; }]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(alertController.textFields[0].text?:@""); }])]; [self presentViewController:alertController animated:YES completion:nil]; } // 頁面是彈出窗口 _blank 處理 - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { if (!navigationAction.targetFrame.isMainFrame) { [webView loadRequest:navigationAction.request]; } return nil; } 復制代碼
三、網頁內容加載進度條和title的實現
//添加監測網頁加載進度的觀察者
[self.webView addObserver:self
forKeyPath:@"estimatedProgress" options:0 context:nil]; //添加監測網頁標題title的觀察者 [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil]; //kvo 監聽進度 必須實現此方法 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && object == _webView) { NSLog(@"網頁加載進度 = %f",_webView.estimatedProgress); self.progressView.progress = _webView.estimatedProgress; if (_webView.estimatedProgress >= 1.0f) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self.progressView.progress = 0; }); } }else if([keyPath isEqualToString:@"title"] && object == _webView){ self.navigationItem.title = _webView.title; }else{ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } //移除觀察者 [_webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))]; [_webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(title))]; 復制代碼
四、JS和OC的交互
- JS調用OC
這個實現主要是依靠WKScriptMessageHandler協議類和WKUserContentController兩個類:WKUserContentController對象負責注冊JS方法,設置處理接收JS方法的代理,代理遵守WKScriptMessageHandler,實現捕捉到JS消息的回調方法,詳情可以看第一步中對這兩個類的介紹。
//這個類主要用來做native與JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//注冊一個name為jsToOcNoPrams的js方法,設置處理接收JS方法的代理
[wkUController addScriptMessageHandler:self name:@"jsToOcNoPrams"]; [wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"]; config.userContentController = wkUController; 注意:遵守WKScriptMessageHandler協議,代理是由WKUserContentControl設置 //通過接收JS傳出消息的name進行捕捉的回調方法 js調OC - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo); } 復制代碼
- OC調用JS
//OC調用JS changeColor()是JS方法名,completionHandler是異步回調block
NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js參數"]; [_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) { NSLog(@"改變HTML的背景色"); }]; //改變字體大小 調用原生JS方法 NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 100]; [_webView evaluateJavaScript:jsFont completionHandler:nil]; 復制代碼
五、本地HTML文件的實現
由於示例Demo的需要以及知識有限,我用僅知的HTML、CSS、JavaScript的一點皮毛寫了一個HTML文件,比較業余,大神勿噴😁😁 小白想學習這方面的知識可以看這里: www.w3school.com.cn/index.html
我用MAC自帶的文本編輯工具,生成一個文件,改后綴名,強轉為.html文件,同時還需要設置文本編輯打開HTML文件時顯示代碼(如下圖),然后編輯代碼。

詳情請前往我的Github:WKWebView的使用
如果我WKWebView使用的總結沒幫到你,你也可以看看下面幾篇文: www.jianshu.com/p/833448c30… www.jianshu.com/p/4fa8c4eb1… www.jianshu.com/p/91cfe58c0…