天天看點

iOS 學習 --- WKWebView使用

WKWebView是蘋果在iOS 8之後推出的架構WebKit中的浏覽器控件,其加載速度比UIWebView快了許多, 但記憶體占用率卻下降很多,也解決了加載網頁時的記憶體洩露問題。現在的項目大多數隻需适配到iOS 8, 是以用WKWebView來替換項目中的UIWebView是很有必要的。

WKWebView常用到的幾個類:

  • WKWebView
  • WKWebViewConfiguration
  • WKUserScript
  • WKUserContentController
  • WKWebsiteDataStore:清理緩存

以及3個代理:

  • WKUIDelegate
  • WKNavigationDelegate
  • WKScriptMessageHandler

WKWebView常用屬性

WKUIDelegate 代理方法

這個代理方法, 主要是用來處理使用系統的彈框來替換JS中的一些彈框的,比如: 警告框, 選擇框, 輸入框, 主要使用的是下面三個代理方法:
// 建立一個新的WebView(标簽帶有 target='_blank' 時,導緻WKWebView無法加載點選後的網頁的問題。)
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    // 接口的作用是打開新視窗委托
    WKFrameInfo *frameInfo = navigationAction.targetFrame;
    if (![frameInfo isMainFrame]) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}
           
/**
 警告框
 在JS端調用alert函數時,會觸發此代理方法。
 JS端調用alert時所傳的資料可以通過message拿到
 在原生得到結果後,需要回調JS,是通過completionHandler回調
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    NSLog(@"%s", __FUNCTION__);
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:@"JS調用alert" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];

    [self presentViewController:alert animated:YES completion:NULL];
    NSLog(@"%@", message);
}
           
/**
 确認框
 JS端調用confirm函數時,會觸發此方法
 通過message可以拿到JS端所傳的資料
 在iOS端顯示原生alert得到YES/NO後
 通過completionHandler回調給JS端
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
    NSLog(@"%s", __FUNCTION__);

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm" message:@"JS調用confirm" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }]];
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }]];
    [self presentViewController:alert animated:YES completion:NULL];

    NSLog(@"%@", message);
}
           
/**
 輸入框
 JS端調用prompt函數時,會觸發此方法
 要求輸入一段文本
 在原生輸入得到文本内容後,通過completionHandler回調給JS
*/

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
    NSLog(@"%s", __FUNCTION__);

    NSLog(@"%@", prompt);
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS調用輸入框" preferredStyle:UIAlertControllerStyleAlert];
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.textColor = [UIColor redColor];
    }];

    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler([[alert.textFields lastObject] text]);
    }]];

    [self presentViewController:alert animated:YES completion:NULL];
}
           

WKNavigationDelegate代理方法

WKNavigationDelegate:最常用,和UIWebViewDelegate功能類似,該代理提供的方法,可以用來追蹤加載過程(頁面開始加載、加載完成、加載失敗)、決定是否執行跳轉。下面會對函數做簡單的說明,并用數字标出調用的先後次序:11-22-33-44-55。

用來追蹤加載過程(頁面開始加載、加載完成、加載失敗)的方法: 

/**
 *  22,頁面開始加載時調用
 *
 *  @param webView    實作該代理的webview
 *  @param navigation 目前navigation
 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  44,當内容開始傳回時調用
 *
 *  @param webView    實作該代理的webview
 *  @param navigation 目前navigation
 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  55,頁面加載完成之後調用
 *
 *  @param webView    實作該代理的webview
 *  @param navigation 目前navigation
 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  4,加載失敗時調用
 *
 *  @param webView    實作該代理的webview
 *  @param navigation 目前navigation
 *  @param error      錯誤
 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"%s", __FUNCTION__);
}
           

頁面跳轉的代理方法: 

/**
 *  1,接收到伺服器跳轉請求之後調用
 *
 *  @param webView      實作該代理的webview
 *  @param navigation   目前navigation
 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  11,在發送請求之前,決定是否跳轉
 *
 *  @param webView          實作該代理的
 *  @param navigationAction 目前navigation
 *  @param decisionHandler  是否調轉block
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSString *hostname = navigationAction.request.URL.host.lowercaseString;
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated
        && ![hostname containsString:@".baidu.com"]) {
        // 對于跨域,需要手動跳轉
        [[UIApplication sharedApplication] openURL:navigationAction.request.URL];

        // 不允許web内跳轉
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
       self.progressView.alpha = 1.0;
        decisionHandler(WKNavigationActionPolicyAllow);
    }

    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  33,在收到響應後,決定是否跳轉。在收到伺服器的響應頭,根據response相關資訊,決定是否跳轉。decisionHandler必須調用,來決定是否跳轉,參數WKNavigationActionPolicyCancel取消跳轉,WKNavigationActionPolicyAllow允許跳轉。
 *
 *  @param webView            實作該代理的webview
 *  @param navigationResponse 目前navigation
 *  @param decisionHandler    是否跳轉block
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

    //    NSLog(@"%@",navigationResponse.response);

    // 如果響應的位址是百度,則允許跳轉
    //    if ([navigationResponse.response.URL.host.lowercaseString isEqual:@"www.baidu.com"]) {
    //
    //        // 允許跳轉
    //        decisionHandler(WKNavigationResponsePolicyAllow);
    //        return;
    //    }
    //    // 不允許跳轉
    //    decisionHandler(WKNavigationResponsePolicyCancel);
    decisionHandler(WKNavigationResponsePolicyAllow);
}
           

其他一些方法

// 當main frame最後下載下傳資料失敗時,會回調
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}

// 用于授權驗證的API,與AFN、UIWebView的授權驗證API是一樣的
// 對于HTTPS的都會觸發此代理,如果不要求驗證,傳預設就行
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
    
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
}

/**
9.0才能使用,web内容進行中斷時會觸發
當web content處理完成時,會回調
在 UIWebView 上當記憶體占用太大的時候,App Process 會 crash,在 WKWebView 上當總體的記憶體占用比較大的時候,WebContent Process 會 crash,進而出現白屏現象。
iOS 9以後 WKNavigtionDelegate 新增了一個回調函數:

*/
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    [webView reload];
    NSLog(@"程序終止");
}
           

WKWebView 執行JS代碼

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSString *jsString = [NSString stringWithFormat:@"showSatisfaction()"];
        
        [self.wkWebView evaluateJavaScript:jsString completionHandler:^(id response, NSError * error) {
            NSLog(@"error: %@", error.description);
            NSLog(@"response: %@", response);
            //js傳回0:js控制跳出彈框 1:傳回上級頁面
            if ([response intValue] == 1) {
                   [self.navigationController popViewControllerAnimated:YES];
               }
        }];
    });
           

WKScriptMessageHandler代理方法

// 從web界面中接收到一個腳本時調用

 -(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    
    //JS調用OC方法
    //message.boby就是JS裡傳過來的參數
    NSLog(@"body:%@",message.body);
    
    if ([message.name isEqualToString:@"Share"]) {
        
            [self ShareWithInformation:message.body];
    
    } else if ([message.name isEqualToString:@"Camera"]) {
        
            [self camera];
    }
    
}
           
 WKScriptMessage有兩個關鍵屬性name 和 body。因為我們給每一個OC方法取了一個name,是以就可以根據name 來區分執行不同的方法。body 中存着JS 要給OC 傳的參數。
-(void)ShareWithInformation:(NSDictionary *)dic{
    
    if (![dic isKindOfClass:[NSDictionary class]]) {
            return;
    }
    
    NSString *title = [dic objectForKey:@"title"];
    NSString *content = [dic objectForKey:@"content"];
    NSString *url = [dic objectForKey:@"url"];
    //在這裡寫分享操作的代碼
    NSLog(@"要分享了哦?");
    //OC回報給JS分享結果
    NSString *JSResult = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
    //OC調用JS
    [self.webView evaluateJavaScript:JSResult completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        
            NSLog(@"%@", error);
    }];
}
           

相關文章

[iOS]WKWebView的使用--API篇

UIWebView、WKWebView使用詳解及性能分析

iOS WKWebView的基本使用

iOS WKWebView(清理緩存)

iOS OC與JS的互動(WKWebview-MessageHandler實作)