WKWebView的使用和各種坑的解決方法(OC+Swift)

雖然WKWebView是在AppleWWDC 2014iOS 8OS X 10.10出來的,是為了解決UIWebView加載速度慢俊戳、占用內(nèi)存大的問題。但是由于之前還要適配iOS7,又不想做兩套加載頁面(主要是因?yàn)閼校┌ッ模跃蜎]有使用∽馄現(xiàn)在項(xiàng)目都適配iOS 8以上了阶女,所以就開始使用WKWebView了,但是發(fā)現(xiàn)在使用的時(shí)候有好多坑哩治,希望這篇文章能帶大家繞過坑秃踩,更好的使用WKWebView

這篇文章主要介紹了以下問題业筏,方便小伙伴們查閱:

  1. WKWebView的基本介紹和使用
  1. WKWebViewJavaScript的交互
  1. 解決WKWebView加載POST請(qǐng)求無法發(fā)送參數(shù)問題

WKWebView的基本介紹和使用

WKWebView的幾個(gè)代理方法

WKWebView是蘋果在iOS 8中引入的新組件憔杨,目的是給出一個(gè)新的高性能的WebView解決方案,擺脫過去 UIWebView的老蒜胖、舊消别、笨重,特別是內(nèi)存占用量巨大的問題翠勉,它使用Nitro JavaScript引擎妖啥,這意味著所有第三方瀏覽器運(yùn)行JavaScript將會(huì)跟safari一樣快。

看到我這篇文章的小伙伴对碌,對(duì)iOS的開發(fā)應(yīng)該有一定的了解荆虱,肯定用過UIWebView,現(xiàn)在就用UIWebViewWKWebView的代理方法做一個(gè)對(duì)比朽们。

  • 加載狀態(tài)的回調(diào)(用來跟蹤頁面加載的過程(頁面開始加載怀读、加載完成、加載失敗的方法)骑脱,還可以決定是否跳轉(zhuǎn)):

    1. 準(zhǔn)備加載頁面
        UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
        WKNavigationDelegate: - webView:didStartProvisionalNavigation:
2. **內(nèi)容開始加載**`(view的過渡動(dòng)畫可在此方法中加載)`
        UIWebViewDelegate: - webViewDidStartLoad:
        WKNavigationDelegate: - webView:didCommitNavigation:
3. **頁面加載完成**`(view的過渡動(dòng)畫的移除可在此方法中進(jìn)行)`
        UIWebViewDelegate: - webViewDidFinishLoad:
        WKNavigationDelegate: - webView:didFinishNavigation:
4. **頁面加載失敗**
        UIWebViewDelegate: - webView:didFailLoadWithError:
        WKNavigationDelegate: - webView:didFailNavigation:withError:
        WKNavigationDelegate: - webView:didFailProvisionalNavigation:withError:

此外菜枷,WKWebKit還有三個(gè)頁面跳轉(zhuǎn)的代理方法:

  • 頁面跳轉(zhuǎn)的代理
    1. 接收到服務(wù)器跳轉(zhuǎn)請(qǐng)求的代理
        WKNavigationDelegate: - webView:didReceiveServerRedirectForProvisionalNavigation:
2. **在收到響應(yīng)后,決定是否跳轉(zhuǎn)的代理**
        WKNavigationDelegate: - webView:decidePolicyForNavigationResponse:decisionHandler:
3. **在發(fā)送請(qǐng)求之前叁丧,決定是否跳轉(zhuǎn)的代理**
        WKNavigationDelegate: - webView:decidePolicyForNavigationAction:decisionHandler:

WKWebView增加的屬性

  1. WKWebViewConfiguration *configuration:初始化WKWebView的時(shí)候的配置啤誊,后面會(huì)用到
  2. WKBackForwardList *backForwardList:相當(dāng)于訪問歷史的一個(gè)列表
  3. double estimatedProgress:進(jìn)度岳瞭,有這個(gè)之后就不用自己寫假的進(jìn)度條了

WKWebView的使用

OC代碼:

    // 創(chuàng)建WKWebView
    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    // 設(shè)置訪問的URL
    NSURL *url = [NSURL URLWithString:@"http://www.reibang.com"];
    // 根據(jù)URL創(chuàng)建請(qǐng)求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // WKWebView加載請(qǐng)求
    [webView loadRequest:request];
    // 將WKWebView添加到視圖
    [self.view addSubview:webView];

Swift代碼:

    // 創(chuàng)建WKWebView
    let webView = WKWebView(frame: UIScreen.mainScreen().bounds)
    // 設(shè)置訪問的URL
    let url = NSURL(string: "http://www.reibang.com")
    // 根據(jù)URL創(chuàng)建請(qǐng)求
    let requst = NSURLRequest(URL: url!)
    // WKWebView加載請(qǐng)求
    webView.loadRequest(requst) 
    // 將WKWebView添加到視圖
    view.addSubview(webView)

可以看到很簡(jiǎn)單,和UIWebView并沒有多少差別蚊锹,然而性能就刷刷刷的提上去了瞳筏,是不是很爽呢?如果你只是簡(jiǎn)單的集成個(gè)Web頁到App牡昆,這些已經(jīng)夠了姚炕。不過很多時(shí)候并沒有那么簡(jiǎn)單,還需要處理各種東西丢烘,那么接著往后看柱宦。


WKWebViewJavaScript的交互

WebKit框架中,有WKWebView可以替換UIKitUIWebViewAppKitWebView播瞳,而且提供了在兩個(gè)平臺(tái)可以一致使用的接口掸刊。WebKit框架使得開發(fā)者可以在原生App中使用Nitro來提高網(wǎng)頁的性能和表現(xiàn),Nitro就是SafariJavaScript引擎,WKWebView不支持JavaScriptCore的方式但提供message handler的方式為JavaScript與Native通信狐史。(這個(gè)引自天狐博客痒给,更多的與UIWebView或者WKWebView的交互方法可以在這里看到。下面部分代碼(例如JS)也是竊取這個(gè)作者的骏全,尊重原著苍柏,所以把原博客地址放這里,與JS交互寫的比我好多了姜贡。)

Native調(diào)用JavaScript方法

原生調(diào)用JavaScript的代碼需要在頁面加載完成之后试吁,就是在 - webView:didFinishNavigation:代理方法里面
OC代碼:

[webView evaluateJavaScript:@"showAlert('奏是一個(gè)彈框')" completionHandler:^(id item, NSError * _Nullable error) {
        // Block中處理是否通過了或者執(zhí)行JS錯(cuò)誤的代碼
    }];

Swift代碼:

webView.evaluateJavaScript("showAlert('奏是一個(gè)彈框')") { (item, error) in
            // 閉包中處理是否通過了或者執(zhí)行JS錯(cuò)誤的代碼
        }   

大家可以看到這段JS代碼是最簡(jiǎn)單的彈出一個(gè)Alert的代碼,后面WKWebView加載POST請(qǐng)求參數(shù)問題中還會(huì)有一個(gè)加載POST請(qǐng)求的JS代碼楼咳,先不要管它了熄捍,請(qǐng)各位看官繼續(xù)往后翻,看看JavaScript怎么調(diào)用Native的方法母怜。

JavaScript調(diào)用Native方法

  • JavaScript的配置

    JavaScript調(diào)用Native的方法就需要前端和Native的小伙伴們配合了余耽,需要前端的小伙伴在JS的方法中調(diào)用:

    window.webkit.messageHandlers.NativeMethod.postMessage("就是一個(gè)消息啊");
    

    這行代碼。請(qǐng)注意苹熏,這個(gè)NativeMethod是和App中要統(tǒng)一的碟贾,配置方法將在下面的Native中書寫。

  • Native App的代碼配置

    下面該Native的代碼的配置了轨域,細(xì)心的小伙伴可能已經(jīng)發(fā)現(xiàn)了袱耽,創(chuàng)建WKWebView的時(shí)候,除了有- initWithFrame:方法外干发,還有一個(gè)高端的方法:- initWithFrame:configuration:方法朱巨。那句名言是誰說的來著:普通玩家選擇推薦配置,高端玩家選擇自定義配置枉长,就當(dāng)是我說的吧(那個(gè)拿鞋的把鞋穿上吧冀续,我承認(rèn)不是我說的??)琼讽。這個(gè)方法就是用來自定義配置的,具體怎么自定義呢沥阳,童鞋們接著往下看吧跨琳。

    OC代碼:

        // 創(chuàng)建配置
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        // 創(chuàng)建UserContentController(提供JavaScript向webView發(fā)送消息的方法)
        WKUserContentController* userContent = [[WKUserContentController alloc] init];
        // 添加消息處理,注意:self指代的對(duì)象需要遵守WKScriptMessageHandler協(xié)議桐罕,結(jié)束時(shí)需要移除
        [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.reibang.com"];
        // 根據(jù)URL創(chuàng)建請(qǐng)求
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        // WKWebView加載請(qǐng)求
        [webView loadRequest:request];
        // 將WKWebView添加到視圖
        [self.view addSubview:webView];
    

    Swift代碼:

        // 創(chuàng)建配置
        let config = WKWebViewConfiguration()
        // 創(chuàng)建UserContentController(提供JavaScript向webView發(fā)送消息的方法)
        let userContent = WKUserContentController()
        // 添加消息處理,注意:self指代的對(duì)象需要遵守WKScriptMessageHandler協(xié)議桂敛,結(jié)束時(shí)需要移除
        userContent.addScriptMessageHandler(self, name: "NativeMethod")
        // 將UserConttentController設(shè)置到配置文件
        config.userContentController = userContent
        // 高端的自定義配置創(chuàng)建WKWebView
        let webView = WKWebView(frame: UIScreen.mainScreen().bounds, configuration: config)
        
        // 設(shè)置訪問的URL
        let url = NSURL(string: "http://www.reibang.com")
        // 根據(jù)URL創(chuàng)建請(qǐng)求
        let requst = NSURLRequest(URL: url!)
        // 設(shè)置代理
        webView.navigationDelegate = self
        // WKWebView加載請(qǐng)求
        webView.loadRequest(requst)
        
        // 將WebView添加到當(dāng)前view
        view.addSubview(webView)
    

    可以看到功炮,添加消息處理的handlername,就是JavaScript中調(diào)用時(shí)候的NativeMethod术唬,這兩個(gè)要保持一致薪伏。請(qǐng)把URL換成你自己的。

    請(qǐng)注意第6行的代碼配置當(dāng)前ViewControllerMessageHandler粗仓,需要服從WKScriptMessageHandler協(xié)議嫁怀,如果出現(xiàn)警告??,請(qǐng)檢查是否服從了這個(gè)協(xié)議借浊。

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

    移除的代碼如下:

    OC代碼:

    [webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];
    

    Swift代碼:

    webView.configuration.userContentController.removeScriptMessageHandlerForName("NativeMethod")
    

    請(qǐng)注意這個(gè)Name和上面創(chuàng)建WKWebView的配置中注冊(cè)的名字是一樣的,要保持對(duì)應(yīng)曙蒸。

    好了捌治,現(xiàn)在萬事俱備,只欠東風(fēng)了纽窟。東風(fēng)是什么呢肖油,就是該在哪兒處理”鄹郏可以看到WKScriptMessageHandler的協(xié)議里面只有一個(gè)方法森枪,就是:

    - userContentController:didReceiveScriptMessage:
    

    相信聰明的你已經(jīng)猜到了。是的趋艘,就是在這個(gè)代理方法里面操作:如果JavaScript執(zhí)行已經(jīng)寫好的:window.webkit.messageHandlers.NativeMethod.postMessage("就是一個(gè)消息啊");這行代碼疲恢,這個(gè)代理方法就會(huì)走,并且會(huì)有個(gè)WKScriptMessage的對(duì)象瓷胧,這個(gè)WKScriptMessage對(duì)象有個(gè)name屬性显拳,拿到之后你會(huì)發(fā)現(xiàn),就是我們注冊(cè)的NativeMethod這個(gè)字符串搓萧,這時(shí)候你就可以手動(dòng)調(diào)用Native的方法了杂数。如果有多個(gè)方法需要調(diào)用的話怎么辦宛畦,看到JavaScriptpostMessage()方法有一個(gè)參數(shù)了沒有,可以根據(jù)這里的參數(shù)來區(qū)分調(diào)用原生App的哪個(gè)方法揍移。
    代碼很簡(jiǎn)單次和,就不寫了。什么那伐?你說你還需要寫踏施?好吧,那我還是貼出來吧:

    OC代碼:

        - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
            // 判斷是否是調(diào)用原生的
            if ([@"NativeMethod" isEqualToString:message.name]) {
                // 判斷message的內(nèi)容罕邀,然后做相應(yīng)的操作
                if ([@"close" isEqualToString:message.body]) {
            
                }
            }
        }
    

    Swift代碼:

        func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
            // 判斷是否是調(diào)用原生的
            if "NativeMethod" == message.name {
                // 判斷message的內(nèi)容畅形,然后做相應(yīng)的操作
                if "close" == message.body as! String {
                
                }
            }
        }
    

    上面的方法就可以獲取到JavaScript發(fā)送的Message了,JavaScript可以這樣調(diào)用:window.webkit.messageHandlers.NativeMethod.postMessage("close");诉探,這時(shí)候上面的代理方法的兩個(gè)if判斷都能通過日熬,不同的操作可增加里面的if語句的分支判斷message的內(nèi)容來進(jìn)行不同的Native代碼的調(diào)用,也就是JavaScriptpostMessage方法的參數(shù)的不同來區(qū)分不同的操作肾胯。

    好了竖席,現(xiàn)在WKWebViewJavaScript的簡(jiǎn)單交互你也會(huì)了。用WKWebView的時(shí)候貌似也還算開心敬肚。但是不要高興的太早毕荐,下面就要有坑了。


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

也許你用UIWebView加載過POST請(qǐng)求的頁面帘皿,感覺并沒有什么難點(diǎn)或者需要注意的地方东跪,那真的是圖樣圖森破了,因?yàn)槲乙策@樣天真過鹰溜。直到我踩了很多坑之后虽填,我才發(fā)現(xiàn)夢(mèng)想與現(xiàn)實(shí)之間的差別,不過沒關(guān)系曹动,我又要說另一句名言了:沒有挖不到的墻角...斋日,咳咳咳,說錯(cuò)了墓陈,請(qǐng)重新來BGM恶守,跟我一起說:沒有解決不了的Bug,只有不努力的碼農(nóng)贡必!(各位架構(gòu)師兔港、高級(jí)開發(fā)工程師請(qǐng)手下留情,我說的碼農(nóng)是我??)

來來來仔拟,先來一發(fā)POST請(qǐng)求加載WebView衫樊。你會(huì)說,這還不easy?下面就來一個(gè)科侈,走起:
OC代碼:

    // 創(chuàng)建WKWebView
    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    // 設(shè)置訪問的URL
    NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
    // 根據(jù)URL創(chuàng)建請(qǐng)求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 設(shè)置請(qǐng)求方法為POST
    [request setHTTPMethod:@"POST"];
    // WKWebView加載請(qǐng)求
    [webView loadRequest:request];
    // 將WKWebView添加到視圖
    [self.view addSubview:webView];

Swift代碼:

    // 創(chuàng)建WKWebView
    let webView = WKWebView(frame: UIScreen.mainScreen().bounds)
    // 設(shè)置訪問的URL
    let url = NSURL(string: "http://www.example.com")
    // 根據(jù)URL創(chuàng)建請(qǐng)求
    let requst = NSMutableURLRequest(URL: url!)
    // 設(shè)置請(qǐng)求方法為POST
    requst.HTTPMethod = "POST"
    // WKWebView加載請(qǐng)求
    webView.loadRequest(requst)
    // 將WKWebView添加到視圖
    view.addSubview(webView)

這樣確實(shí)加載POST請(qǐng)求的網(wǎng)頁成功了(注意請(qǐng)把鏈接換成自己的)载佳,你一定露出了得意的笑容。但是騷年臀栈,不要高興的太早蔫慧,這只是一個(gè)簡(jiǎn)單的POST請(qǐng)求,還沒有添加參數(shù)呢权薯。于是乎姑躲,你又說:那更簡(jiǎn)單,在第9行插入如下代碼即可(比方說這個(gè)接口是登錄):

OC代碼:

    // 設(shè)置請(qǐng)求參數(shù)
    [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];

Swift代碼:

    // 設(shè)置請(qǐng)求參數(shù)
    requst.HTTPBody = "username=aaa&password=123".dataUsingEncoding(NSUTF8StringEncoding)

這種方法在UIWebView里面是沒有問題的崭闲,所以你認(rèn)為在這里也應(yīng)該是沒有問題的肋联。從理論上講應(yīng)該是這樣的,但是我要恭喜你了刁俭,這是WKWebView的Bug,讓你給碰到了韧涨。這里寫的POST請(qǐng)求沒有問題牍戚,但是就是不會(huì)把這兩個(gè)參數(shù)傳上去的,不信你可以試試(截止我發(fā)表這篇博客的日期虑粥,iOS 9.3并沒有修復(fù)此問題)如孝。

好了,不廢話了(其實(shí)已經(jīng)說了很多廢話了)娩贷,下面看解決辦法(如果你需要適配iOS 8請(qǐng)直接使用方法2):

  1. 使用NSURLSession發(fā)送一個(gè)請(qǐng)求第晰,然后把請(qǐng)求下來的數(shù)據(jù)當(dāng)作本地HTML加載
  2. 使用JavaScript解決WKWebView無法發(fā)送POST參數(shù)問題

1. 使用NSURLSession解決WKWebView無法POST參數(shù)的問題(性能和結(jié)果都可能有問題,不推薦使用)

當(dāng)發(fā)現(xiàn)POST無法傳遞參數(shù)的時(shí)候彬祖,我首先想到的是換個(gè)方法來茁瘦,就是用一般的請(qǐng)求方式:NSURLSession發(fā)送請(qǐng)求,然后把接收到的數(shù)據(jù)轉(zhuǎn)化成字符串储笑,然后再用WKWebView加載甜熔。大家可能已經(jīng)看出來了,需要把整個(gè)網(wǎng)頁放到內(nèi)存中或著放到本地然后再加載突倍,所以肯定消耗內(nèi)存呀腔稀。下面貼代碼吧:

OC代碼:

    // 創(chuàng)建WKWebView
    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    // 將WKWebView添加到當(dāng)前View
    [self.view addSubview:webView];
    // 設(shè)置訪問的URL
    NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
    // 根據(jù)URL創(chuàng)建請(qǐng)求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 設(shè)置請(qǐng)求方法為POST
    [request setHTTPMethod:@"POST"];
    // 設(shè)置請(qǐng)求參數(shù)
    [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];
    
    // 實(shí)例化網(wǎng)絡(luò)會(huì)話
    NSURLSession *session = [NSURLSession sharedSession];
    // 創(chuàng)建請(qǐng)求Task
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        // 將請(qǐng)求到的網(wǎng)頁數(shù)據(jù)用loadHTMLString 的方法加載
        NSString *htmlStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        [webView loadHTMLString:htmlStr baseURL:nil];
    }];
    // 開啟網(wǎng)絡(luò)任務(wù)
    [task resume];

Swift代碼:

    // 創(chuàng)建WKWebView
    let webView = WKWebView(frame: UIScreen.mainScreen().bounds)
    // 設(shè)置訪問的URL
    let url = NSURL(string: "http://www.example.com")
    // 根據(jù)URL創(chuàng)建請(qǐng)求
    let requst = NSMutableURLRequest(URL: url!)
    // 設(shè)置請(qǐng)求方法為POST
    requst.HTTPMethod = "POST"
    // 設(shè)置請(qǐng)求參數(shù)
    requst.HTTPBody = "username=aaa&password=123".dataUsingEncoding(NSUTF8StringEncoding)
    // 將WKWebView添加到視圖
    view.addSubview(webView)
    
    // 實(shí)例化網(wǎng)絡(luò)會(huì)話
    let session = NSURLSession.sharedSession()
  
    // 創(chuàng)建請(qǐng)求Task
    let task = session.dataTaskWithRequest(requst) { (data, response, error) in
        webView.loadHTMLString(String(data: data!, encoding: NSUTF8StringEncoding)!, baseURL: nil)
    }
    task.resume()

當(dāng)你用iOS 9以上的設(shè)備的時(shí)候,貌似完全沒有一點(diǎn)問題羽历,只是需要請(qǐng)求下來再放而已焊虏。但是注意前提條件:iOS 9,當(dāng)你用iOS 8的時(shí)候秕磷,發(fā)現(xiàn)你的網(wǎng)頁的樣式和JavaScript事件全部沒有了诵闭。是不是有一種呵呵的沖動(dòng),那你就盡情呵呵吧跳夭。如果你要適配iOS 8涂圆,那么這個(gè)方法也不符合你的氣質(zhì)们镜。

其實(shí)這個(gè)東西和加載本地網(wǎng)頁無法加載CSS樣式和JS一樣,如果你也加載本地HTML文件出現(xiàn)問題润歉,請(qǐng)查看Jay神WKWebView使用遇到的坑模狭。盡給別人打廣告了,呵呵踩衩,聲明一下敖鲤摹:我跟這些人木有關(guān)系,只是為了方便大家查閱而已驱富,誰讓我那么的大公無私呢??锚赤。

好了,好了褐鸥,來看一個(gè)更好的解決辦法吧:

2. 使用JavaScript解決WKWebView無法發(fā)送POST參數(shù)問題

開始之前我先說一下實(shí)現(xiàn)思路线脚,方便大家理解,如果出錯(cuò)了也能知道錯(cuò)誤的地方:

  1. 將一個(gè)包含JavaScriptPOST請(qǐng)求的HTML代碼放到工程目錄中
  2. 加載這個(gè)包含JavaScriptPOST請(qǐng)求的代碼到WKWebView
  3. 加載完成之后叫榕,用Native調(diào)用JavaScriptPOST方法并傳入?yún)?shù)來完成請(qǐng)求
  1. 創(chuàng)建包含JavaScriptPOST請(qǐng)求的HTML代碼

    相關(guān)代碼:

    <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>
```
將這段代碼拷貝下來浑侥,然后粘貼到文本編輯器中,名字可以隨意起晰绎,比方說保存為:JSPOST.html寓落,然后拷貝到工程目錄中,記得選擇對(duì)應(yīng)的Target和勾選Copy items if needed(默認(rèn)應(yīng)該是勾選的)荞下。這時(shí)候伶选,就可以用這段JavaScript代碼來發(fā)送帶參數(shù)的POST請(qǐng)求了。

  1. 將對(duì)應(yīng)的JavaScript代碼通過加載本地網(wǎng)頁的形式加載到WKWebView

    OC代碼:

    // JS發(fā)送POST的Flag尖昏,為真的時(shí)候會(huì)調(diào)用JS的POST方法(僅當(dāng)?shù)谝淮蔚臅r(shí)候加載本地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添加到當(dāng)前View
    [self.view addSubview:self.webView];
    

    Swift代碼:

    // JS發(fā)送POST的Flag仰税,為真的時(shí)候會(huì)調(diào)用JS的POST方法(僅當(dāng)?shù)谝淮蔚臅r(shí)候加載本地JS)
    needLoadJSPOST = true
    // 創(chuàng)建WKWebView
    webView = WKWebView(frame: UIScreen.mainScreen().bounds)
    //設(shè)置代理
    webView.navigationDelegate = self
    // 獲取JS路徑
    let path = NSBundle.mainBundle().pathForResource("JSPOST", ofType: "html")
    // 獲得html內(nèi)容
    do {
        
        let html = try String(contentsOfFile: path!, encoding: NSUTF8StringEncoding)
        // 加載js
        webView.loadHTMLString(html, baseURL: NSBundle.mainBundle().bundleURL)
    } catch { }
    // 將WKWebView添加到當(dāng)前View
    view.addSubview(webView)
    

    這段代碼就相當(dāng)于把工程中的JavaScript腳本加載到WKWebView中了,后面就是看怎么用了会宪。(請(qǐng)注意換成您的文件名)

  2. Native調(diào)用JavaScript腳本并傳入?yún)?shù)來完成POST請(qǐng)求

    還記得 WKWebView和JavaScript的交互這一節(jié)嘛肖卧?現(xiàn)在該Native調(diào)用JavaScript了,如果忘記了掸鹅,請(qǐng)往前翻溫故一下:- webView:didFinishNavigation:代理表明頁面已經(jīng)加載完成塞帐,我們?cè)谶@里操作,下面上代碼:

    OC代碼:

    // 加載完成的代理方法
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        // 判斷是否需要加載(僅在第一次加載)
        if (self.needLoadJSPOST) {
            // 調(diào)用使用JS發(fā)送POST請(qǐng)求的方法
            [self postRequestWithJS];
            // 將Flag置為NO(后面就不需要加載了)
            self.needLoadJSPOST = NO;
        }
    }
    
    // 調(diào)用JS發(fā)送POST請(qǐng)求
    - (void)postRequestWithJS {
        // 發(fā)送POST的參數(shù)
        NSString *postData = @"\"username\":\"aaa\",\"password\":\"123\"";
        // 請(qǐng)求的頁面地址
        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) {
        
        }];
    }
    
    

    Swift代碼:

    // 加載完成的代理方法
    func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
        // 判斷是否需要加載(僅在第一次加載)
        if needLoadJSPOST {
            // 調(diào)用使用JS發(fā)送POST請(qǐng)求的方法
            postRequestWithJS()
            // 將Flag置為NO(后面就不需要加載了)
            needLoadJSPOST = false
        }
    }
    // 調(diào)用JS發(fā)送POST請(qǐng)求
    func postRequestWithJS() {
        // 發(fā)送POST的參數(shù)
        let postData = "\"username\":\"aaa\",\"password\":\"123\""
        // 請(qǐng)求的頁面地址
        let urlStr = "http://www.postexample.com"
        // 拼裝成調(diào)用JavaScript的字符串
        let jscript = "post('\(urlStr)', {\(postData)});"
        // 調(diào)用JS代碼
        webView.evaluateJavaScript(jscript) { (object, error) in
            
        }
    }
    

    好了巍沙,到目前為止你的請(qǐng)求就發(fā)出去了葵姥。相信后面的版本會(huì)解決這個(gè)問題,但是現(xiàn)在你要用的話也得有辦法句携,誰讓已經(jīng)入了Apple的坑呢榔幸,誰讓UIWebView太不給力了呢.


寫在最后:
當(dāng)時(shí)選擇WKWebView就是為了提高性能,但是沒有想到遇到這么多坑,從看iOS 9才解決了iOS 8無法加載本地樣式的問題削咆,有時(shí)候蘋果解決問題的速度還有略慢的牍疏,到現(xiàn)在POST請(qǐng)求參數(shù)都發(fā)不出去也真是不應(yīng)該。不過沒辦法拨齐,先解決了鳞陨,說不定iOS 10 出來之后解決了呢。(我雖然有iOS 10的設(shè)備瞻惋,但是我還沒有測(cè)試厦滤,感興趣的小伙伴們可以試試)。大家如果有什么問題歼狼,歡迎留言提問掏导。謝謝支持!


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羽峰,一起剝皮案震驚了整個(gè)濱河市趟咆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梅屉,老刑警劉巖忍啸,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異履植,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悄晃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門玫霎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妈橄,你說我怎么就攤上這事庶近。” “怎么了眷蚓?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵鼻种,是天一觀的道長。 經(jīng)常有香客問我沙热,道長叉钥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任篙贸,我火速辦了婚禮投队,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爵川。我一直安慰自己敷鸦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扒披,像睡著了一般值依。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碟案,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天愿险,我揣著相機(jī)與錄音,去河邊找鬼蟆淀。 笑死拯啦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的熔任。 我是一名探鬼主播褒链,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼疑苔!你這毒婦竟也來了甫匹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤惦费,失蹤者是張志新(化名)和其女友劉穎兵迅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體薪贫,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恍箭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞧省。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扯夭。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞍匾,靈堂內(nèi)的尸體忽然破棺而出交洗,到底是詐尸還是另有隱情,我是刑警寧澤橡淑,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布构拳,位于F島的核電站,受9級(jí)特大地震影響梁棠,放射性物質(zhì)發(fā)生泄漏置森。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一掰茶、第九天 我趴在偏房一處隱蔽的房頂上張望暇藏。 院中可真熱鬧,春花似錦濒蒋、人聲如沸盐碱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓮顽。三九已至县好,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暖混,已是汗流浹背缕贡。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拣播,地道東北人晾咪。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像贮配,于是被迫代替她去往敵國和親谍倦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容