WKWebView與js交互之完美解決方案

 隨着H5功能愈發的強大,沒進行過混合開發的小夥們都很差意思說本身可以獨立進行iOS的app開發,在iOS7操做系統下,經常使用的native,js交互框架有easy-js,WebViewJavascriptBridge,以及結合javaScriptCore的框架。easy-js 很早的一個框架了,已經好幾年沒有人維護了,裏面有不少隱藏很深的坑,新人若是沒有用過的話,建議不要再用了。主要是js新建一個隱藏的iframe,經過攔截url的形式進行交互。WebViewJavascriptBridge是網上很火的一個交互庫,使用的人較多,可是對於js基礎較弱的小夥伴來講,底層不是太好理解。底層和easy-js同樣都是經過創新一個隱藏的iframe經過截取url來進行交互。缺點這裏就暫時不說了,用的很少,體會不夠深入。嘿嘿。javaScriptCore中JSExport進行交互,這種方式比較簡單易懂,也是我我的比較推崇 的一種方式。若是app最低版本操做系統是iOS7的小夥伴,建議本身搜一下相關知識點哦。可是WKWebView不可以利用javaScriptCore交互,是否是很坑爹哦,嗚嗚。 
   因爲本身去年的強力推進,今年咱們的一系列app最低操做系統都是從iOS8開始,因此今天重點和你們分享一下我是如何實現WKWebView與js交互的。 
js發送消息給native的代理方法是: 
方法1,javascript

- (void)userContentController:(WKUserContentController *)userContentController
      didReceiveScriptMessage:(WKScriptMessage *)message
  •  

主要是js方法消息調用native的方法。css

native調用js方法傳遞參數主要經過以下方法: 
方法2html

[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {


    }];
  •  

相信這兩個方法你們在網上已經看到過不少遍了,貌似能夠解決與js交互的問題,實則否則,這兩個方法並無真正的幫咱們解決交互的問題。由於在js發送消息給native的時候,有時候須要經過回調來獲取相應的信息,僅僅靠上面兩個方法是沒有辦法知足的,也可能會有小夥伴說,先經過上面方法1發送消息個native而後,再使用方法2發送消息給js不就行了麼,不行的,這樣的話,js調用native方法時,和native發送消息時候並無時間前後的約定,不能保證,js獲取相關返回值的時候,必定能拿到值。 
   我一直在想如何能有一個與js調用native函數相關連的回調呢。功夫不負有心人,偶然看到H5 ,DOM能夠綁定事件,後來想能不能綁定自定義事件呢,一搜果真能夠,參考博客以下: 
http://www.zhangxinxu.com/wordpress/2012/04/js-dom自定義事件/ 
順着這個思路,每一次js方法調用native方法的時候,我都爲這個js方法綁定一個對應的callBack方法,這樣的話,同時在發送的消息中告訴native須要回調,native方法就能夠執行完相關的方法後,直接回調相應的 callBack方法,並攜帶相關的參數,這樣就能夠完美的進行交互了。這裏我主要寫了一個JKEventHandler的js類,腳本內容以下:java

var JKEventHandler ={

callNativeFunction:function(functionString,params,callBack){

    var methodName = (functionString.replace(/function\s?/mi,"").split("("))[0];
    var callBackName =methodName + 'CallBack';
    var message;

    if(!callBack){

        message = {'methodName':methodName,'params':params};
        window.webkit.messageHandlers.JKEventHandler.postMessage(message);

    }else{
        message = {'methodName':methodName,'params':params,'callBackName':callBackName};
        if(!Event._listeners[callBackName]){
        Event.addEvent(callBackName, function(data){

                       callBack(data);

                       });
        }
        window.webkit.messageHandlers.JKEventHandler.postMessage(message);
    }


},

callBack:function(callBackName,data){

    Event.fireEvent(callBackName,data);

},

removeAllCallBacks:function(data){
    Event._listeners ={};
}

};



var Event = {

_listeners: {},


addEvent: function(type, fn) {
    if (typeof this._listeners[type] === "undefined") {
        this._listeners[type] = [];
    }
    if (typeof fn === "function") {
        this._listeners[type].push(fn);
    }

    return this;
},


fireEvent: function(type,param) {
    var arrayEvent = this._listeners[type];
    if (arrayEvent instanceof Array) {
        for (var i=0, length=arrayEvent.length; i<length; i+=1) {
            if (typeof arrayEvent[i] === "function") {
                arrayEvent[i](param);
            }
        }
    }

    return this;
},

removeEvent: function(type, fn) {
    var arrayEvent = this._listeners[type];
    if (typeof type === "string" && arrayEvent instanceof Array) {
        if (typeof fn === "function") {
            for (var i=0, length=arrayEvent.length; i<length; i+=1){
                if (arrayEvent[i] === fn){
                    this._listeners[type].splice(i, 1);
                    break;
                }
            }
        } else {
            delete this._listeners[type];
        }
    }

    return this;
}
};

callNativeFunction: 這個函數主要是js調用native方法的時候進行調用的。若是有回調的話,須要在傳入的參數中寫出來哦。 
callBack:主要是用來觸發對應js方法回調函數的。ios

removeAllCallBacks: 主要是在要銷燬全部的callback事件時調用的。 
以個人demo爲例git

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>iOS and Js</title>
    <style type="text/css">
      * {
        font-size: 40px;
      }
    </style>
  </head>

  <body>

    <div style="margin-top: 100px">
      <h1 style="color: red;">教你如何用H5與OC進行交互,而且把H5輸入的內容顯示到當前的控制器上</h1><br/>
      <div><input type="button" value="sendInfoToNative" onclick="sendInfoToNative()"></div>
      <br/>
      <div><input type="button"  value="getInfoFromNative" onclick="getInfoFromNative()"></div>
      <br/>
       <div><input type="button" value="cleanAllCallBacks" onclick="cleanAllCallBacks()"></div>
       <br/>
      <div><input type="button" value="點擊觸發JS方法(callJsConfirm)" onclick="callJsConfirm()"></div><br/>
    </div>
    <br/>
    <div>
      <div><input type="button" value="點擊觸發JS輸入方法(callJsInput) " onclick="callJsInput()"></div><br/>
    </div>

    <br/>
    <div id="SwiftDiv">
      <span id="jsParamFuncSpan" style="color: red; font-size: 50px;"></span>
    </div>

    <script type="text/javascript">
      function sendInfoToNative() {


        var params ={'Phone':'13566668888'};

       JKEventHandler.callNativeFunction(arguments.callee.toString(),params,null);

      }

    function getInfoFromNative(){

     var params = {'Phone':'13933333333'};
     JKEventHandler.callNativeFunction(arguments.callee.toString(),params,function(data){
                                      alert(data);
                                      });


    }

    function callJsConfirm() {
      if (confirm('confirm', 'Objective-C call js to show confirm')) {
        document.getElementById('jsParamFuncSpan').innerHTML
        = 'true';
      } else {
        document.getElementById('jsParamFuncSpan').innerHTML
        = 'false';
      }

    }

    function callJsInput() {
      var response = prompt('Hello', '請輸入你的名字:');
      document.getElementById('jsParamFuncSpan').innerHTML = response;
      alert (response);

    }

    function cleanAllCallBacks(){

    JKEventHandler.removeAllCallBacks();

    }
      </script>
  </body>
</html>
  •  

在js中調用getInfoFromNative 這個方法既能夠發送參數給native,同時也能夠從native獲取參數 
具體實現:程序員

function getInfoFromNative(){

     var params = {'Phone':'13933333333'};
     JKEventHandler.callNativeFunction(arguments.callee.toString(),params,function(data){
                                      alert(data);
                                      });


    }

同時呢,經過JKEventHandler的轉換,在JKEventHandler+Demo.m文件中有一個同名的函數,只不過是參數不同:github

- (void)getInfoFromNative:(id)params :(void(^)(id response))callBack{
    NSLog(@"params %@",params);
    NSString *str = @"'Hi Jack!'";
    callBack(str);

}
  •  

就這樣,我在native方法裏能夠獲取到js傳遞來的參數,同時經過callBack我也能夠傳遞參數給js。 
另外呢在JKEventHandler文件裏,我寫了一個事件分發函數,主要就是爲了解決多個js方法交互的問題。感興趣的小夥伴能夠看看個人demo哦。 
另外須要你們注意的是,WKWebView所在的ViewController即將被銷燬的時候,也就是WKWebView即將被銷燬的時候,必定要記得調用以下方法銷燬全部的callback事件哦:web

[_webView evaluateJavaScript:@"JKEventHandler.removeAllCallBacks();" completionHandler:^(id _Nullable data, NSError * _Nullable error) {


    }];//刪除全部的回調事件
  •  

俗話說的話,代碼就是程序員最好的老師,這裏我就很少說了,Demo地址 
如何使用了cocoapod,能夠:app

pod "JKWKWebViewHandler"
相關文章
相關標籤/搜索