iOS WebViewJavascriptBridge初步嘗試與圖文詳細講解


JS和OC的交互這是個永恆話題,使用場景也是越來越多,如今一些reactnative、vue框架等,都是在重點結合原生與H5的混合使用。

那么,如何快捷方便的使用兩者交互是一個很重要的關鍵點。

 

1、傳統上的交互使用:

- OC調用JS:

webView對象通過調用stringByEvaluatingJavaScriptFromString這個方法執行一段JS代碼實現交互。

如:

OC代碼:

[self.webView stringByEvaluatingJavaScriptFromString:@"ocCallJS({'name':'xiaoxiao'})"];

JS代碼:

</script>
   function ocCallJS(data) {
    var obj = eval(data);
    alert(obj.name);
   }
<script>

 

這種方式對一些簡單場景比較適用,也很方便。

- JS調用OC:

webView攔截url鏈接,獲取內容,再處理邏輯

如:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

實現以上webView的代理方法,當webView每次開始加載URL時會進入這個方法,我們便可以在這個方法實現JS調用OC。

舉例大概如下:

JS代碼:

OC代碼:

(圖片來源於網絡)

這種JS調用OC的方法的缺點十分明顯,需要繁瑣地解釋字符串得到相應的方法名和傳值,且調用的方法也不能傳遞返回值;

但優點是:不需要等待頁面加載完才觸發,當相應的代碼被運行就能調用OC的方法(相比 JavaScriptCore而言,下文會講到)。

 

2、蘋果推薦的框架--JavaScriptCore

JavaScriptCore是蘋果在iOS7時新推出用以實現JS和iOS代碼交互的框架,十分簡單高效。

使用這種,需要導入JavaScriptCore.framework框架。

oc調用js時:

-(void)webViewDidFinishLoad:(UIWebView *)webView  
{  
    //網頁加載完成調用此方法  
      
    //首先創建JSContext 對象(此處通過當前webView的鍵獲取到jscontext)  
    JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];  
    NSString *alertJS=@"alert('test js OC')"; //准備執行的js代碼  
    [context evaluateScript:alertJS];//通過oc方法調用js的alert  
      
} 

這句話的意思,是在webview加載結束,注入一段js代碼,和傳統方式有異曲同工之處。

 

js調用oc時:

這個是這個框架的優勢重點。這里基本解決了兩者的調用,並且能夠實現,調用之后的回調處理。

舉例:使用block方式比較簡單,也是我比價推薦的一種,但是要注意防止循環引用問題的發生。


HTML文件按鈕代碼

 <button onclick="myAction(str);" style="">點擊按鈕返回上一個頁面</button>

OC中代碼

- (void)webViewDidFinishLoad:(UIWebView *)webView方法中對block塊進行代碼實現.

  __weak typeof(self)temp = self;
  self.context[@"myAction"] = ^(NSString *str){
      //如果有參數,就是str
      [temp.navigationController popViewControllerAnimated:YES];

  };

 

但!!!這里有個坑,就是oc調用js,必須是html加載完成之后(webViewDidFinishLoad)才可以。

 

3、WKScriptMessageHandler

注意:使用時必須iOS8+以及WKWebView(其實WKWebView並不支持方法二),如果用wk,我們可以用WKWebView的WKScriptMessageHandler 來實現交互。

第一步:初始化WKWebView,調用addScriptMessageHandler:name:方法,name為js中的方法名,如scan:

- (void)setupWKWebView{
  WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
  configuration.userContentController = [[WKUserContentController alloc] init];
  [configuration.userContentController addScriptMessageHandler:self name:@"funcname"];

  WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
  webView.UIDelegate = self;
}

oc調用js:

//OC調用JS
NSString *JSResult = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];

[self.webView evaluateJavaScript:JSResult completionHandler:^(id _Nullable result, NSError * _Nullable error) {
    NSLog(@"%@", error);
}];

 

 

js調用oc:

h5:

func(){
    window.webkit.messageHandlers.funcname.postMessage() 
}

  // 注意:這里funcname是方法名,必須和oc里定義的一致

 

oc:在WKScriptMessageHandler代理方法,當js調用scan方法時,會回調此代理方法:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
  if ([message.name isEqualToString:@"funcname"]) {
    
//調用原生掃碼  
  }
}

 

4、優秀的第三方框架--WebViewJavascriptBridge

到了這里,就是本文的重點了,介紹一下WebViewJavascriptBridge

GitHub地址

WebViewJavascriptBridge同時支持UIWeView和WKWebView,無論是JS調用OC還是OC調用JS,都可以正常傳值和返回值;而且在頁面加載時只要JS代碼被運行就可以進行交互,上面遇到的缺點和坑在這里都被掩埋的,所以是現在處理交互的主流做法。

網上我找的資料,很多也是從官方demo衍生出來的,本文也不例外,不同的是,我會在代碼里,詳細的添加各種注釋,保證大家能快速的理解。

 

使用介紹:

1、准備文件

 

2、js代碼截取片段

解釋:這段代碼是必須的,申明交互直接拷貝即可,處理交互部分,需要改動,關鍵就是和oc端協商的方法名,以及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 = 'https://__bridge_loaded__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }

    <!-- 處理交互  方法名要和ios內定義的對應-->
    setupWebViewJavascriptBridge(function(bridge) {

        <!--處理 oc 調用 js -->
        bridge.registerHandler('registerAction', function(data, responseCallback) {
            //處理oc給的傳參
            alert('oc請求js  傳值參數是:'+data)                               
            var responseData = { 'result':'handle success' }
            //處理完,回調傳值給oc
            responseCallback(responseData)
        })

        var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
        callbackButton.innerHTML = '點擊我,我會調用oc的方法'
        callbackButton.onclick = function(e) {
            e.preventDefault()                                 
            <!--處理 js 調用 oc -->
            bridge.callHandler('loginAction', {'userId':'zhangsan','name': '章三'}, function(response) {
                 //處理oc過來的回調
                 alert('收到oc過來的回調:'+response)
            })
        }
    })

 

3、OC代碼

- pod導入框架

pod 'WebViewJavascriptBridge'

- import頭部

#import "WebViewJavascriptBridge.h"

- viewDidload

    //初始化  WebViewJavascriptBridge
    if (_bridge) { return; }
    [WebViewJavascriptBridge enableLogging];    
    _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
    [_bridge setWebViewDelegate:self];    
    
    //請求加載html,注意:這里h5加載完,會自動執行一個調用oc的方法
    [self loadExamplePage:webView];    
    
    //申明js調用oc方法的處理事件,這里寫了后,h5那邊只要請求了,oc內部就會響應
    [self JS2OC];    
  
    //模擬操作:2秒后,oc會調用js的方法
    //注意:這里厲害的是,我們不需要等待html加載完成,就能處理oc的請求事件;此外,webview的request 也可以在這個請求后面執行(可以把上面的[self loadExamplePage:webView]放到[self OC2JS]后面執行,結果是一樣的)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self OC2JS];        
    });

 - JS  調用  OC

-(void)JS2OC{
    /*
     含義:JS調用OC
     @param registerHandler 要注冊的事件名稱(比如這里我們為loginAction)
     @param handel 回調block函數 當后台觸發這個事件的時候會執行block里面的代碼
     */
    [_bridge registerHandler:@"loginAction" handler:^(id data, WVJBResponseCallback responseCallback) {
        // data js頁面傳過來的參數  假設這里是用戶名和姓名,字典格式
        NSLog(@"JS調用OC,並傳值過來");
        
        // 利用data參數處理自己的邏輯
        NSDictionary *dict = (NSDictionary *)data;
        NSString *str = [NSString stringWithFormat:@"用戶名:%@  姓名:%@",dict[@"userId"],dict[@"name"]];
        [self renderButtons:str];
        
        // responseCallback 給js的回復
        responseCallback(@"報告,oc已收到js的請求");
    }];

}

-  OC  調用  JS

-(void)OC2JS{
    /*
     含義:OC調用JS
     @param callHandler 商定的事件名稱,用來調用網頁里面相應的事件實現
     @param data id類型,相當於我們函數中的參數,向網頁傳遞函數執行需要的參數
     注意,這里callHandler分3種,根據需不需要傳參數和需不需要后台返回執行結果來決定用哪個
     */
    
    //[_bridge callHandler:@"registerAction" data:@"我是oc請求js的參數"];
    [_bridge callHandler:@"registerAction" data:@"uid:123 pwd:123" responseCallback:^(id responseData) {
        NSLog(@"oc請求js后接受的回調結果:%@",responseData);
    }];
    
}

 

這里的關鍵點就是:OC和JS商定的方法名要統一,兩端要合作一下。

這里的舉例,我都用到了處理后的回調,大家運行demo的時候注意看日志文件。

 

源碼下載:點擊這里獲取Demo

 

以上幾種方法就到這里里,如果需求比較簡單,不一定需要使用最后一種,靈活運用更方便~  

enjoy


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM