UIWebView中JS與OC交互 WebViewJavascriptBridge的使用

1、綜述

  如今不少的應用都會在多種平臺上發佈,因此不少程序猿們都開始使用Hybrid App的設計模式。就是在app上嵌入網頁,只要寫一份網頁代碼,就能夠跑在不一樣的系統上。在iOS中,app可能是經過WebView來加載網頁,因爲功能需求等緣由,代碼中少不得要和跟網頁交互。javascript

2、原理

  在iOS中,本地調用Javascript語言,是經過UIWebView中的實例方法stringByEvaluatingJavaScriptFromString:來實現的,該方法經過字符串對象的形式傳入JS代碼。java

1
[webView stringByEvaluatingJavaScriptFromString:@ "Math.random();" ];

   而JS調用本地的代碼,則並無現成的API,而是須要間接地經過一些方法來實現。咱們利用UIWebView的代理方法,當UIWebView發起的全部網絡請求,均可以經過delegate函數在Native層獲得通知。這樣,咱們就能夠在UIWebView內發起一個自定義的網絡請求,好比:'wvjbscheme://__BRIDGE_LOADED__'。因而在UIWebView的delegate函數中,咱們攔截url,只要發現是咱們自定義的url,就不進行內容的加載,轉而執行相應的調用邏輯。git

3、WebViewJavascriptBridge的使用

一、WebViewJavascriptBridge簡介github

  WebViewJavascriptBridge支持到iOS6以前的版本的,用於支持native的iOS與javascript交互,接下來說講WebViewJavascriptBridge的基本原理及應該如何去使用,包括iOS端的使用和JS端的使用。web

  首先,看看WebViewJavascriptBridge.m中Webview代理攔截的代碼:設計模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- ( void )webView:(WebView *)webView decidePolicyForNavigationAction:( NSDictionary  *)actionInformation request:( NSURLRequest  *)request frame:(WebFrame *)frame decisionListener:( id <WebPolicyDecisionListener>)listener
{
     if  (webView != _webView) {  return ; }
     
     NSURL  *url = [request URL];
     if  ([_base isCorrectProcotocolScheme:url]) {
         if  ([_base isBridgeLoadedURL:url]) {
             [_base injectJavascriptFile];
         else  if  ([_base isQueueMessageURL:url]) {
             NSString  *messageQueueString = [ self  _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
             [_base flushMessageQueue:messageQueueString];
         else  {
             [_base logUnkownMessage:url];
         }
         [listener ignore];
     else  if  (_webViewDelegate && [_webViewDelegate respondsToSelector: @selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
         [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
     else  {
         [listener use];
     }
}

   WebViewJavascriptBridge是經過webview的代理攔截scheme,而後注入相應的JS,在攔截後,經過先經過-isBridgeLoadedURL:方法判斷URL是不是須要bridge的URL,如果,則經過injectJavascriptFile方法注入JS;不然判斷URL是不是隊列消息,如果,則執行查詢命令JS並刷新消息隊列;若是都不匹配,URL被識別爲未知的消息。網絡

 

二、WebViewJavascriptBridge的使用app

  首先,要在JS中接入這個框架,這段代碼是不變的框架

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
  *  此爲js接入框架的函數
  */
function setupWebViewJavascriptBridge(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 =  'wvjbscheme://__BRIDGE_LOADED__' ;
     document.documentElement.appendChild(WVJBIframe);
     setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

 

  而後OC要調用到的JS函數要在下面函數中使用bridge.registerHandler來註冊,並且JS須要調用的OC方法也要在下面的函數中用bridge.callHandler調用dom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
  *  OC調用的JS函數需在此處註冊,調用OC方法也須要在此處調用
  */
     setupWebViewJavascriptBridge(function(bridge) {
         var uniqueId = 1
         function log(message, data) {
             var log = document.getElementById( 'log' )
             var el = document.createElement( 'div' )
             el.className =  'logLine'
             el.innerHTML = uniqueId++ +  '. '  + message +  ':<br/>'  + JSON.stringify(data)
             if  (log.children.length) { log.insertBefore(el, log.children[0]) }
             else  { log.appendChild(el) }
         }
                                  
         //註冊一個給OC調用的函數,不帶參數
         bridge.registerHandler( 'WebViewDidLoad' ,function() {
             log( "WebViewDidLoad" )
         })
         
         //註冊一一個給OC調用的函數,接受OC傳來的一個參數和一個回調處理
         bridge.registerHandler( 'OC_Call_JS' , function(data, responseCallback) {
             log( 'oc調用js -' , data)
             var responseData = {  'Javascript response' : 'oc調用JS成功!'  }
             log( 'js被調用後響應-' , responseData)
             responseCallback(responseData)
         })
 
         document.body.appendChild(document.createElement( 'br' ))
 
         var callbackButton = document.getElementById( 'buttons' ).appendChild(document.createElement( 'button' ))
         callbackButton.innerHTML =  'JS_Call_ObjC'
         callbackButton.onclick = function(e) {
             e.preventDefault()
             log( 'JS call OC' )
             //此處調用OC方法
             bridge.callHandler( 'JS_Call_ObjC' , { 'foo' 'bar' }, function(response) {
                 log( 'JS call OC sucess and get OC rsp' , response)
             })
         }
     })

  須要注意的是:在setupWebViewJavascriptBridge(function(bridge) {}函數體內的代碼不能有錯誤,否則會致使不任何回調,不打印日誌(JS的是腳本語言,跑到錯的地方就不跑了)。

 

  OC部分,首先打開框架的日誌系統,而後關聯webView

1
2
3
[WebViewJavascriptBridge enableLogging];
     
     _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];

   JS須要調用的OC方法,要在OC代碼中註冊

1
2
3
4
[_bridge registerHandler:@ "JS_Call_ObjC"  handler:^( id  data, WVJBResponseCallback responseCallback) {
         NSLog (@ "JS調用OC: %@" , data);
         responseCallback(@ "OC被調用後響應:調用成功!" );
     }];

   而想要調用JS中註冊過的函數,在須要的地方用實例方法callHandler調用就能夠了

1
2
3
4
id  data = @{ @ "OC調用JS" : @ "Hi there, JS!"  };
[_bridge callHandler:@ "OC_Call_JS"  data:data responseCallback:^( id  response) {
     NSLog (@ "testJavascriptHandler responded: %@" , response);
}];

 

4、小結

  最近由於項目須要,正在邊學邊作Hybrid App,恰好用到這個第三方,就寫了篇文分享出來,但願能幫到剛剛入手的人,以上實例的demo地址https://github.com/GarenChen/WebViewJSBridgeDemo喜歡的順手給個star ^_^;

推薦一些閱讀:

JSBridge——Web與Native交互之iOS篇:http://www.jianshu.com/p/9fd80b785de1

Hybrid App 開發模式:http://www.tuicool.com/articles/riE3Yn

WebViewJavascriptBridge:https://github.com/marcuswestin/WebViewJavascriptBridge

相關文章
相關標籤/搜索