iOS-WKWebView(post請求的一個Bug)

WKWebView

1 在性能、穩(wěn)定性、功能方面有很大提升
2 和 Safari 相同的 JavaScript 引擎,允許JavaScript的Nitro庫加載并使用(UIWebView中限制);

WKWebView使用

// 創(chuàng)建webview
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
// 創(chuàng)建請求
NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
// 加載網(wǎng)頁
[webView loadRequest:request];
// 將webView添加到界面
[self.view addSubview:webView];

WKWebView操作JS
WKWebView加載JS

//JS文件路徑
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
//讀取JS文件內(nèi)容
NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];
//創(chuàng)建用戶腳本對象,
//WKUserScriptInjectionTimeAtDocumentStart :HTML文檔創(chuàng)建后,完成加載前注入,類似于<head>中
//WKUserScriptInjectionTimeAtDocumentEnd :HTML文件完成加載后注入,類似于<body>中
WKUserScript *script = [[WKUserScript alloc] initWithSource:jsContent injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
//添加用戶腳本
[webView.configuration.userContentController addUserScript:script];

WKWebView執(zhí)行JS方法

//執(zhí)行JS方法
[webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
    //result為執(zhí)行js方法的返回值
    if(error){
        NSLog(@"Success");
    }else{
        NSLog(@"Fail");
    }
}];

WKWebView代理方法
WKNavigationDelegate 協(xié)議 導(dǎo)航監(jiān)聽

WKNavigationDelegate
#pragma mark - WKNavigationDelegate
// 頁面開始加載時調(diào)用
#UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"%s",__FUNCTION__);
}

// 內(nèi)容開始返回時調(diào)用(view的過渡動畫可在此方法中加載)
#UIWebViewDelegate: - webViewDidStartLoad:
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"%s",__FUNCTION__);
}
// 頁面加載完成時調(diào)用(view的過渡動畫的移除可在此方法中進行)
#UIWebViewDelegate: - webViewDidFinishLoad:
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"%s",__FUNCTION__);
}
// 頁面加載失敗時調(diào)用
#UIWebViewDelegate: - webView:didFailLoadWithError:
#WKNavigationDelegate: - webView:didFailNavigation:withError:
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"%s",__FUNCTION__);
}

三個頁面跳轉(zhuǎn)的代理方法:

#pragma mark WKNavigation 當web視圖需要響應(yīng)身份驗證跳轉(zhuǎn)時調(diào)用
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * credential))completionHandler{
}
#pragma mark 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
#pragma mark 在收到響應(yīng)后,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
}
#pragma mark 在發(fā)送請求之前,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//需執(zhí)行decisionHandler的block。
}

Native調(diào)用JavaScript方法

原生調(diào)用JavaScript的代碼需要在頁面加載完成之后,就是在 - webView:didFinishNavigation:代理方法里面
OC代碼:
[webView evaluateJavaScript:@"showAlert('奏是一個彈框')" completionHandler:^(id item, NSError * _Nullable error) {
        // Block中處理是否通過了或者執(zhí)行JS錯誤的代碼
    }];

JavaScript調(diào)用Native方法

JavaScript的配置
window.webkit.messageHandlers.NativeMethod.postMessage("就是一個消息啊");
這個NativeMethod是和App中要統(tǒng)一的,配置方法將在下面的Native中書寫。

Native App的代碼配置
創(chuàng)建WKWebView除了有- initWithFrame:方法外,還有一個高端的方法:- initWithFrame:configuration:方法

      // 創(chuàng)建配置
      WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
      // 創(chuàng)建UserContentController(提供JavaScript向webView發(fā)送消息的方法)
      WKUserContentController* userContent = [[WKUserContentController alloc] init];
      // 添加消息處理,注意:self指代的對象需要遵守WKScriptMessageHandler協(xié)議,結(jié)束時需要移除
      [userContent addScriptMessageHandler:self name:@"NativeMethod"];
      // 將UserConttentController設(shè)置到配置文件
      config.userContentController = userContent;
      // 高端的自定義配置創(chuàng)建WKWebView
      WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
      // 設(shè)置訪問的URL
      NSURL *url = [NSURL URLWithString:@"http://www.itdecent.cn"];
      // 根據(jù)URL創(chuàng)建請求
      NSURLRequest *request = [NSURLRequest requestWithURL:url];
      // WKWebView加載請求
      [webView loadRequest:request];
      // 將WKWebView添加到視圖
      [self.view addSubview:webView];

可以看到,添加消息處理的handler的name,就是JavaScript中調(diào)用時候的NativeMethod,這兩個要保持一致。請把URL換成你自己的。

請注意第6行的代碼配置當前ViewController為MessageHandler,需要服從WKScriptMessageHandler協(xié)議,如果出現(xiàn)警告??,請檢查是否服從了這個協(xié)議。

注意!注意!注意:上面將當前ViewController設(shè)置為MessageHandler之后需要在當前ViewController銷毀前將其移除,否則會造成內(nèi)存泄漏。

移除的代碼如下:
[webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];
      

WKUIDelegate 協(xié)議 網(wǎng)頁監(jiān)聽


WKUIDelegate
    #pragma mark - WKUIDelegate
  /// 創(chuàng)建一個新的WebView
  - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
      return nil;
  }
  - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler {

  }
  /// 輸入框
  - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
  }
  /// 確認框
  - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {

  }
  /// 警告框
  - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {

  }

加載順序

百度測試

WKWebView增加的屬性

1 WKWebViewConfiguration *configuration:初始化WKWebView的時候的配置,后面會用到
2 WKBackForwardList *backForwardList:相當于訪問歷史的一個列表
3 double estimatedProgress:進度,有這個之后就不用自己寫假的進度條了

解決WKWebView加載POST請求無法發(fā)送參數(shù)問題

使用JavaScript解決WKWebView無法發(fā)送POST參數(shù)問題
1 將一個包含JavaScript的POST請求的HTML代碼放到工程目錄中
2 加載這個包含JavaScript的POST請求的代碼到WKWebView
3 加載完成之后,用Native調(diào)用JavaScript的POST方法并傳入?yún)?shù)來完成請求
  • 創(chuàng)建包含JavaScript的POST請求的HTML代碼
 <html>
 <head>
     <script>
         //調(diào)用格式: post('URL', {"key": "value"});
         function post(path, params) {
             var method = "post";
             var form = document.createElement("form");
             form.setAttribute("method", method);
             form.setAttribute("action", path);

             for(var key in params) {
                 if(params.hasOwnProperty(key)) {
                     var hiddenField = document.createElement("input");
                     hiddenField.setAttribute("type", "hidden");
                     hiddenField.setAttribute("name", key);
                     hiddenField.setAttribute("value", params[key]);

                     form.appendChild(hiddenField);
                 }
             }
             document.body.appendChild(form);
             form.submit();
         }
     </script>
 </head>
 <body>
 </body>
</html>
  • 將對應(yīng)的JavaScript代碼通過加載本地網(wǎng)頁的形式加載到WKWebView
 // JS發(fā)送POST的Flag,為真的時候會調(diào)用JS的POST方法(僅當?shù)谝淮蔚臅r候加載本地JS)
 self.needLoadJSPOST = YES;
 // 創(chuàng)建WKWebView
 self.webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
 //設(shè)置代理
 self.webView.navigationDelegate = self;
 // 獲取JS所在的路徑
 NSString *path = [[NSBundle mainBundle] pathForResource:@"JSPOST" ofType:@"html"];
 // 獲得html內(nèi)容
 NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
 // 加載js
 [self.webView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
 // 將WKWebView添加到當前View
 [self.view addSubview:self.webView];
  • Native調(diào)用JavaScript腳本并傳入?yún)?shù)來完成POST請求
Native調(diào)用JavaScript
 // 加載完成的代理方法
 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
     // 判斷是否需要加載(僅在第一次加載)
     if (self.needLoadJSPOST) {
         // 調(diào)用使用JS發(fā)送POST請求的方法
         [self postRequestWithJS];
         // 將Flag置為NO(后面就不需要加載了)
         self.needLoadJSPOST = NO;
     }
 }
 // 調(diào)用JS發(fā)送POST請求
 - (void)postRequestWithJS {
     // 發(fā)送POST的參數(shù)
     NSString *postData = @"\"username\":\"aaa\",\"password\":\"123\"";
     // 請求的頁面地址
     NSString *urlStr = @"http://www.postexample.com";
     // 拼裝成調(diào)用JavaScript的字符串
     NSString *jscript = [NSString stringWithFormat:@"post('%@', {%@});", urlStr, postData];

     // NSLog(@"Javascript: %@", jscript);
     // 調(diào)用JS代碼
     [self.webView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
     }];
 }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容