iOS-WKWebView與JavaScript交互的簡單使用

在移動端開發(fā)中,有時可能考慮到開發(fā)效率,會在原生應(yīng)用中集成Web界面,這樣只需要web開發(fā)人員寫完之后,原生只需要加載出來就可以了。但是,也存在native和js之間的交互情況,下面是我在解決交互中使用的方法,和遇見的問題。

在原生用加載web界面,在iOS8之后,我們有兩種選擇,一種是UIKit里的UIWebView,一種是WebKit的WKWebView,其中WKWebView是在iOS8之后才推出的新的web展示控件,目的也很明確,就是為了解決UIWebView加載速度慢,消耗內(nèi)存高的問題,據(jù)說同時加載100個頁面,WKWebView內(nèi)存的消耗量,要比UIWebView少很多。相對而言,如果不支持iOS8之前的版本的話,選擇WKWebView可能會更好一點。

閑話少說,上代碼,Demo中含有兩種方式,一種不借助三方庫,直接用WKWebView,一種是借助三方庫。另外,本篇只是講了一些nativeJavascript之間的交互問題,對于WKWebView的使用,會在以后的文章中繼續(xù)更新。demo地址

方式一: 不借助三方庫,直接使用WKWebView的相關(guān)代理,進(jìn)行交互。(參考demo中Native文件夾)

  • html代碼(參考demo中Native.html文件)
<html>
    <!--描述網(wǎng)頁信息-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>WKWebViewDemo</title>
        <style>
            *{
                font-size: 50px;
            }
        
            .btn{height:80px; width:80%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white}
        </style>
        <script src="Native.js"></script>
        
    </head>
    <!--網(wǎng)頁具體內(nèi)容-->
    <body>
        <div>
            <button class="btn" type="button" onclick="btnClick()">點我調(diào)用OC</button>
        </div>
        <br/>
        <div id="div1"></div>
    </body>
</html>
  • native調(diào)用javascript

    • 1.JS中注冊要被OC調(diào)用的方法
     //JS響應(yīng)OC的調(diào)用
    function OCCallJS(msg){
      document.getElementById('div1').innerText = msg ? msg : '收到了來自O(shè)C的調(diào)用'
      //如果OC需要在調(diào)用了js之后,獲取返回值,可以通過return
      return 'JS收到了OC的調(diào)用,并返回了我'
    }
    
    • 2.OC調(diào)用JS中已經(jīng)注冊的方法
     -(void)click {
         //OC 主動調(diào)用JS
         [_wkWebView evaluateJavaScript:@"OCCallJS('JS被調(diào)用了')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
             //response是js方法OCCallJS的返回值
             NSLog(@"response:%@,error:%@",response,error);
         }];
     }
    
  • javascript調(diào)用native

    • 1.在OC中注冊JS需要調(diào)用的方法
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 20, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds)-120) configuration:config];
    self.wkWebView.navigationDelegate = self;
    [self.view addSubview:self.wkWebView];
    //注冊方法,必須在HTML加載之前添加
    WKUserContentController *userCC = self.wkWebView.configuration.userContentController;
    //JSCallOC,是javascript 主動調(diào)用OC時,
    [userCC addScriptMessageHandler:self name:@"JSCallOC"];
    [self loadLocalHtmlWithFileName:@"Native"];
    
    • 2.JS 調(diào)用在OC中注冊過的方法
    function btnClick() {
      /**
       * JS 調(diào)用 OC,同樣也需要現(xiàn)在OC注冊方法名,然后才能在JS中調(diào)用
       * 原型:window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
       */
      window.webkit.messageHandlers.JSCallOC.postMessage('JS主動調(diào)用OC')
    }
    

方式二: 借助三方庫,推薦使用 WebViewJavascriptBridge(參考demo中JSBridge文件夾)

  • html代碼(參考demo中JSBridge.html文件)
<html>
    <!--描述網(wǎng)頁信息-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>WKWebViewDemo</title>
        <style>
            *{
                font-size: 50px;
            }
        
            .btn{height:80px; width:80%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white}
        </style>
        <script src="JSBridge.js"></script>
        
    </head>

    <!--網(wǎng)頁具體內(nèi)容-->
    <body>
        <div>
            <button class="btn" type="button" onclick="btnClick()">點我調(diào)用OC</button>
        </div>
        <br/>
        <div id="div1"></div>
    </body>
</html>
  • native調(diào)用javascript

  • 1.JS中注冊要被OC調(diào)用的方法

    • 首先在JSBridge.js定義一個全局可用的方法變量
    //這部分是固定的
    var iosBridge = function (callback) {
          if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
          if (window.WVJBCallbacks) { return  window.WVJBCallbacks.push(callback); }
          window.WVJBCallbacks = [callback];
          var WVJBIframe = document.createElement('iframe');
          WVJBIframe.style.display = 'none';
          WVJBIframe.src = 'https://__bridge_loaded__';
          document.documentElement.appendChild(WVJBIframe);
          setTimeout(function() {         
                document.documentElement.removeChild(WVJBIframe) 
          }, 0)
     }
    
    • JSBridge.js中注冊提供給OC調(diào)用的方法。OC調(diào)用JS中的注冊方法,JS有兩種響應(yīng)方式,分別為:
      (1) JS接收到OC的調(diào)用請求,需要通過回調(diào)把一些數(shù)據(jù)給OC

      /**
       *方法原型:bridge.registerHandler("handlerName", function(data,responseData) { ... })
       *@first param: 'OCCallJS', 是OC調(diào)用JS的方法名,需要事先注冊
       *@second param: 回調(diào)函數(shù),其中data 是OC通過 ‘OCCallJS’ 方法,傳過來的值, ’responseCallBack‘ 是JS回調(diào)給OC的方法,通過’responseCallBack‘可以給OC傳遞一個值
      */
      bridge.registerHandler('OCCallJS', function(data,responseCallBack){
          console.log('js is called by oc,data from oc:' + data)
          document.getElementById('div1').innerText = data + '并準(zhǔn)備向JS要一個橘子'
          responseCallBack('并向JS要一個橘子')
      })
      

      (2) JS接收到OC的調(diào)用請求,無需回調(diào)傳值的,分為需要傳參,和不需要傳參兩種,當(dāng)然你也可以直接把不需要的部分傳nil

      /**
       *JS 方法原型:bridge.registerHandler("handlerName", function(responseData) { ... })
       *@first param: 'OCCallJS', 是OC調(diào)用JS的方法名,需要事先注冊
       *@second param: 回調(diào)函數(shù),其中data 是OC通過 ‘OCCallJS’ 方法,傳過來的值,可能為null
      */
      bridge.registerHandler('OCCallJS', function (data){
            document.getElementById('div1').innerText = data ? data : 'Objective-C主動給JS一個蘋果,并且不要回報'
      })
      
  • 2.OC調(diào)用JS中已經(jīng)注冊的方法(OC中調(diào)用方法,必須要和JS中注冊的方法參數(shù)相匹配,否則會報錯)。OC端方法原型有三種:

    //1
    [bridge callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)callback];
    //2
    [bridge callHandler:(NSString)handlerName data:(id)data
    //3
    [bridge callHandler:(NSString)handlerName] 
    

    (1) 針對于JS注冊方法1

    /**注冊方法1必須對應(yīng)`OC`中的方法,如果`OC`沒有`callback`,`JS`端會報錯.
       *OC方法原型:[bridge callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)callback]
       *@first param: 'OCCallJS', 是OC調(diào)用的JS方法名
       *@second param data: 是需要通過OCCallJS方法,傳遞給JS的數(shù)據(jù)
       *@third param responseCallback: JS 收到調(diào)用之后,回調(diào)給OC的值
       */
    [self.bridge callHandler:@"OCCallJS" data:@"Objective-C主動給JS一個蘋果" responseCallback:^(id responseData) 
         NSLog(@"Objective-C給JS一個蘋果后,%@",responseData);
         self.label.text = [NSString stringWithFormat:@"Objective-C給JS一個蘋果后,%@",responseData];
    }];
    

    (2) 針對于JS 注冊方法2

     /**
       *此方法對應(yīng)OC中下面兩個方法
       *OC方法原型:[bridge callHandler:(NSString*)handlerName data:(id)data] or [bridge callHandler:(NSString*)handlerName]
       *如果是前者 則對應(yīng)的JS回調(diào)函數(shù)data中有值,后者,則為null 
       */
    [self.bridge callHandler:@"OCCallJS" data:@"Objective-C主動給JS一個蘋果,并且不要回報"]; 
    或者
    [self.bridge callHandler:@"OCCallJS"];
    
  • javascript調(diào)用native

    • 1.在OC中注冊JS需要調(diào)用的方法
      /**js 調(diào)用原生,獲取數(shù)據(jù),通過回調(diào)給JS
        *OC方法原型- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler
        *@first param handlerName: 'JSCallOC', 是JS調(diào)用的OC方法名
        *@second param handler: JS調(diào)用OC的回調(diào),包括JS傳過來的數(shù)據(jù)data,和OC給JS回調(diào)傳值。
        */
       [self.bridge registerHandler:@"JSCallOC" handler:^(id data, WVJBResponseCallback responseCallback) {
        //其中data可能為nil,如果不想在此給JS傳值,可responseCallback(nil);
        self.label.text = [NSString stringWithFormat:@"%@,并準(zhǔn)備向Objective-C要一個蘋果",data];
        responseCallback(@"并向Objective-C要了一個蘋果");
       }];
      
    • 2.JS 調(diào)用在OC中注冊過的方法
      同樣JS調(diào)用OC的原型方法,也有三種
      //1
      bridge.callHandler("handlerName")
      //2
      bridge.callHandler("handlerName", data) 
      //3
      bridge.callHandler("handlerName", data, function responseCallback(responseData) 
      
      下面的三種方法實例,分別對應(yīng)上面的三種方法
      //1
      bridge.callHandler('JSCallOC')
      //2
      bridge.callHandler('JSCallOC','JS主動給Objective-C一個橘子')
      //3,如果OC的注冊方法中,有回調(diào)函數(shù)中有值,則必須采用此方法,否則js會報錯
      bridge.callHandler('JSCallOC','JS主動給Objective-C一個橘子',function(responseData){
            document.getElementById('div1').innerText = 'JS主動給Objective-C一個橘子,' + responseData
      })
      

注意點

  1. 不管是原生還是用三方庫,native需要調(diào)用的javascript的方法,貌似必須要在HTML中提前注冊,尤其是單頁面應(yīng)用,因為單頁面應(yīng)用只有一個HTML,界面基本上都組件化渲染。
  2. javascript調(diào)用原生時,同樣也需要在調(diào)用前,需要先注冊。
最后編輯于
?著作權(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)容