iOS H5容器:UIWebView和WKWebView
UIWebView的基本用法
1、載入網頁
UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; webView.delegate = self; [self.view addSubview:webView]; //網路地址 NSURL *url = [[NSURL alloc] initWithString:@"http://www.taobao.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [webView loadRequest:request];
2、UIWebViewDelegate幾個常用的代理方法
//進行載入前的預判斷,如果返回YES,則會進入後續流程(StartLoad,FinishLoad)。如果返回NO,這不會進入後續流程。 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; //開始載入網頁 - (void)webViewDidStartLoad:(UIWebView *)webView;//載入完成 - (void)webViewDidFinishLoad:(UIWebView *)webView; //載入失敗 - (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error;
3、Native呼叫JS中的方法
比如我們在載入的HTML檔案中有如下js程式碼:
<script type="text/javascript"> function hello(){ alert("你好!"); } function helloWithName(name){ alert(name+ ",你好!"); } </script>
我們可以呼叫<code>- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;</code>函式進行js呼叫。
[webView stringByEvaluatingJavaScriptFromString:@"hello()"]; [webView stringByEvaluatingJavaScriptFromString:@"helloWithName('jack')"];
js程式碼不一定要在js檔案中預留,也可以在程式碼中通過字串的形式進行呼叫,比如下面:
//自定義js函式 NSString *jsString = @"function sayHello(){ \ alert('jack11') \ } \ sayHello()"; [_webView stringByEvaluatingJavaScriptFromString:jsString]; NSString *jsString = @" var p = document.createElement('p'); \ p.innerText = 'New Line'; \ document.body.appendChild(p); \ "; [_webView stringByEvaluatingJavaScriptFromString:jsString];
4、JS中呼叫Naitve的方法
具體讓js通知native進行方法呼叫,我們可以讓js產生一個特殊的請求。可以讓Native程式碼可以攔截到,而且不然使用者察覺。業界一般的實現方案是在網頁中載入一個隱藏的iframe來實現該功能。通過將iframe的src指定為一個特殊的URL,實現在<code>- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;</code>方案中進行攔截處理。對應的js呼叫程式碼如下:function loadURL(url) { var iFrame; iFrame = document.createElement("iframe"); iFrame.setAttribute("src", url); iFrame.setAttribute("style", "display:none;"); iFrame.setAttribute("height", "0px"); iFrame.setAttribute("width", "0px"); iFrame.setAttribute("frameborder", "0"); document.body.appendChild(iFrame); // 發起請求後這個iFrame就沒用了,所以把它從dom上移除掉 iFrame.parentNode.removeChild(iFrame); iFrame = null; }
比如我們在js程式碼中,呼叫一下兩個js方法:
function iOS_alert() {//呼叫自定義對話方塊 loadURL("alert://abc"); } function call() {// js中進行撥打電話處理 loadURL("tel://17715022071"); }
當你觸發以上方法的時候,就會進入webview的代理方法中進行攔截。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSURL * url = [request URL]; if ([[url scheme] isEqualToString:@"alert"]) {//攔截請求,彈出自定義對話方塊 UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"test" message:[url host] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; return NO; }else if([[url scheme] isEqualToString:@"tel"]){//攔截撥打電話請求 BOOL result = [[UIApplication sharedApplication] openURL:url]; if (!result) { NSLog(@"您的裝置不支援打電話"); } else { NSLog(@"電話打了"); } return NO; } return YES; }
這樣我們就可以讓js進行native的呼叫。
WKWebView的基本用法
1、載入網頁
WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds]; NSURL *url = [NSURL URLWithString:@"http://www.taobao.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [webView loadRequest:request]; [self.view addSubview:webView];
2、幾個常用的代理方法
/** * 根據webView、navigationAction相關資訊決定這次跳轉是否可以繼續進行,這些資訊包含HTTP傳送請求,如頭部包含User-Agent,Accept,refer * 在傳送請求之前,決定是否跳轉的代理 * @param webView * @param navigationAction * @param decisionHandler */ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ decisionHandler(WKNavigationActionPolicyAllow); } /** * 這個代理方法表示當客戶端收到伺服器的響應頭,根據response相關資訊,可以決定這次跳轉是否可以繼續進行。 * 在收到響應後,決定是否跳轉的代理 * @param webView * @param navigationResponse * @param decisionHandler */ - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ decisionHandler(WKNavigationResponsePolicyAllow); } /** * 準備載入頁面。等同於UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType * * @param webView * @param navigation */ - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ } /** * 這個代理是伺服器redirect時呼叫 * 接收到伺服器跳轉請求的代理 * @param webView * @param navigation */ - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ } - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{ } /** * 內容開始載入. 等同於UIWebViewDelegate: - webViewDidStartLoad: * * @param webView * @param navigation */ - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{ } /** * 頁面載入完成。 等同於UIWebViewDelegate: - webViewDidFinishLoad: * * @param webView * @param navigation */ - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{ } /** * 頁面載入失敗。 等同於UIWebViewDelegate: - webView:didFailLoadWithError: * * @param webView * @param navigation * @param error */ - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{ } - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0){ } /* 我們看看WKUIDelegate的幾個代理方法,雖然不是必須實現的,但是如果我們的頁面中有呼叫了js的alert、confirm、prompt方法,我們應該實現下面這幾個代理方法,然後在原來這裡呼叫native的彈出窗,因為使用WKWebView後,HTML中的alert、confirm、prompt方法呼叫是不會再彈出視窗了,只是轉化成ios的native回撥代理方法 */ #pragma mark - WKUIDelegate - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"h5Container" message:message preferredStyle:UIAlertControllerStyleAlert]; // [alertView addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { // textField.textColor = [UIColor redColor]; // }]; [alertView addAction:[UIAlertAction actionWithTitle:@"我很確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }]]; [self presentViewController:alertView animated:YES completion:nil]; }
顯然WKWebView的代理方法提供了比UIWebView顆粒度更細的方法。讓開發者可以進行更加細緻的配置和處理。
3 、Native呼叫JS中的方法
WKWebView提供的呼叫js程式碼的函式是:
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;
比如我們在載入的HTML檔案中有如下js程式碼:
<script type="text/javascript"> function hello(){ alert("你好!"); } function helloWithName(name){ alert(name + ",你好!"); } </script>
我們可以呼叫如下程式碼進行js的呼叫:
[_wkView evaluateJavaScript:@"hello()" completionHandler:^(id item, NSError * error) { }]; [_wkView evaluateJavaScript:@"helloWithName('jack')" completionHandler:^(id item, NSError *error) { }];
同UIWebView一樣,我們也可以通過字串的形式進行js呼叫。
NSString *jsString = @"function sayHello(){ \ alert('jack11') \ } \ sayHello()"; [_wkView evaluateJavaScript:jsString completionHandler:^(id item, NSError *error) { }]; jsString = @" var p = document.createElement('p'); \ p.innerText = 'New Line'; \ document.body.appendChild(p); \ "; [_wkView evaluateJavaScript:jsString completionHandler:^(id item, NSError *error) { }];
4、JS中呼叫Naitve的方法
除了和UIWebView載入一個隱藏的ifame之外,WKWebView自身還提供了一套js呼叫native的規範。
我們可以在初始化WKWebView的時候,給他設定一個config引數。
//高階配置 //建立配置 WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; //建立UserContentController(提供javaScript向webView傳送訊息的方法) WKUserContentController *userContent = [[WKUserContentController alloc] init]; //新增訊息處理,注意:self指代的是需要遵守WKScriptMessageHandler協議,結束時需要移除 [userContent addScriptMessageHandler:self name:@"NativeMethod"]; //將UserContentController設定到配置檔案中 config.userContentController = userContent; //高階的自定義配置建立WKWebView _wkView = [[YXWKView alloc] initWithFrame:self.view.bounds configuration:config]; NSURL *url = [NSURL URLWithString:@"http://localhost:8080/myDiary/index.html"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [_wkView loadRequest:request]; _wkView.UIDelegate = self; _wkView.navigationDelegate = self; [self.view addSubview:_wkView];
我們在js可以通過NativeMethod這個Handler讓js程式碼呼叫native。
比如在js程式碼中,我新增了一個方法
<script type="text/javascript"> function invokeNativeMethod(){ window.webkit.messageHandlers.NativeMethod.postMessage("我要呼叫native的方法"); } </script>
觸發以上方法的時候,會在native以下方法中進行攔截處理。
#pragma mark - WKScriptMessageHandler - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ //這裡就是使用高階配置,js呼叫native的處理地方。我們可以根據name和body,進行橋協議的處理。 NSString *messageName = message.name; if ([@"NativeMethod" isEqualToString:messageName]) { id messageBody = message.body; NSLog(@"%@",messageBody); } }
UIWebView和WKWebView的比較和選擇
WKWebView是蘋果在WWDC2014釋出會中釋出IOS8的時候公佈WebKit時候使用的新型的H5容器。它與UIWebView相比較,擁有更快的載入速度和效能,更低的記憶體佔用。將UIWebViewDelegate和UIWebView重構成了14個類,3個協議,可以讓開發者進行更加細緻的配置。
但是他有一個最致命的缺陷,就是WKWebView的請求不能被NSURLProtocol截獲。而我們團隊開發的app中對於H5容器最佳的優化點主要就在於使用NSURLProtocol技術對於H5進行離線包的處理和H5的圖片和Native的圖片公用一套快取的技術。因為該問題的存在,目前我們團隊還沒有使用WKWebView代替UIWebVIew。
以上為學習文章,隨著時間的遷移,好多內容已經過時。
'UIWebView' was deprecated in iOS 12.0: No longer supported; please adopt WKWebView.
UIWebView已經過時