WebViewJavaScriptBridge 基本使用

在 iOS 開發 Hybrid App 的時候,有兩個 WebView 可以選擇。
UIWebView & WKWebView。

這兩個 WebView 控件,可以完全只借助 iOS 自帶的框架進行 OC & JS 交互。

  1. UIWebView 使用 javaScriptCore.
  2. WKWebView 使用 WKUserContentController.

UIWebView 原生的交互原理
通過一個 JSContext 獲取 UIWebView 的 JS 執行上下文。
然后通過這個上下文,進行 OC & JS 的雙端交互。

 _jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        NSLog(@"%@",@"獲取 WebView JS 執行環境失敗了!");
    };

WKWebView 原生交互原理

通過 userContentController 把需要觀察的 JS 執行函數注冊起來。
然后通過一個協議方法,將所有注冊過的 JS 函數執行的參數傳遞到此協議方法中。

注冊 需要 觀察的 JS 執行函數

 [webView.configuration.userContentController addScriptMessageHandler:self name:@"jsFunc"];

在 JS 中調用這個函數并傳遞參數數據

window.webkit.messageHandlers.jsFunc.postMessage({name : "李四",age : 22});

OC 中遵守 WKScriptMessageHandler 協議。

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

此協議方法里的 WKScriptMessage 有 name & body 兩個屬性。 name 可以用來判斷是哪個 JSFunc 調用了。body 則是 JSFunc 傳遞到 OC 的參數。


WebViewJavaScriptBridge

WebViewJavaScriptBridge 用于 WKWebView & UIWebView 中 OC 和 JS 交互。
它的基本原理是:

把 OC 的方法注冊到橋梁中,讓 JS 去調用。
把 JS 的方法注冊在橋梁中,讓 OC 去調用。

WebViewJavascriptBridge 基本原理

注冊自己,調用它人。


WebViewJavaScriptBridge 使用的基本步驟

  1. 首先在項目中導入 WebViewJavaScriptBridge 框架
pod ‘WebViewJavascriptBridge’
  1. 導入頭文件 #import <WebViewJavascriptBridge.h>
  2. 建立 WebViewJavaScriptBridge 和 WebView 之間的關系。
_jsBridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
  1. 在HTML 文件中,復制粘貼這兩段 JS 函數。
function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback]; // 創建一個 WVJBCallbacks 全局屬性數組,并將 callback 插入到數組中。
        var WVJBIframe = document.createElement('iframe'); // 創建一個 iframe 元素
        WVJBIframe.style.display = 'none'; // 不顯示
        WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; // 設置 iframe 的 src 屬性
        document.documentElement.appendChild(WVJBIframe); // 把 iframe 添加到當前文導航上。
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
    
    // 這里主要是注冊 OC 將要調用的 JS 方法。
    setupWebViewJavascriptBridge(function(bridge){
       
    });

到此為止,基本的準備工作就做完了。現在需要往橋梁中注入 OC 方法 和 JS 函數了。


往橋梁中注入 OC 方法 和 JS 函數

往橋梁中注入 OC 方法。

 [_jsBridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"dataFrom JS : %@",data[@"data"]);
        
        responseCallback(@"掃描結果 : www.baidu.com");
    }];

這段代碼的意思:

  1. scanClick 是 OC block 的一個別名。
  2. block 本身,是 JS 通過某種方式調用到 scanClick 的時候,執行的代碼塊。
  3. data ,由于 OC 這端由 JS 調用,所以 data 是 JS 端傳遞過來的數據。
  4. responseCallback OC 端的 block 執行完畢之后,往 JS 端傳遞的數據。

往橋梁中注入 JS 函數.

OC 方法,在 OC 中注入。JS 的方法所以必然就需要在 JS 中注入的。(好像是廢話)
在 JS 的方法如何注入到橋梁呢?

之前,在準備工作的時候,有兩段 JS 代碼。
需要在第二段 JS 代碼中,注入 JS 的函數。

// 這里主要是注冊 OC 將要調用的 JS 方法。
    setupWebViewJavascriptBridge(function(bridge){
        // 聲明 OC 需要調用的 JS 方法。
        bridge.registerHanlder('testJavaScriptFunction',function(data,responseCallback){
            // data 是 OC 傳遞過來的數據.
            // responseCallback 是 JS 調用完畢之后傳遞給 OC 的數據
            alert("JS 被 OC 調用了.");
            responseCallback({data: "js 的數據",from : "JS"});
        })
    });

這段代碼的意思:

  1. testJavaScriptFunction 是注入到橋梁中 JS 函數的別名。以供 OC 端調用。
  2. 回調函數的 data。 既然 JS 函數由 OC 調用,所以 data 是 OC 端傳遞過來的數據。
  3. responseCallback 。 JS 調用在被 OC 調用完畢之后,向 OC 端傳遞的數據。

基本就是:

OC 端注冊 OC 的方法,OC 端調用 JS 的函數。
JS 端注冊 JS 的函數,JS 端調用 OC 的方法。


場景

JS -> OC 的交互

在 HTML 中,有個按鈕,點擊這個按鈕,修改 NavigationBar 的顏色。

  1. 在 OC 端,往橋梁注入一個修改 NavigationBar 顏色的 block.
  2. 在 JS 端,調用這個 block,來間接的達到修改顏色的目的。
![![WebViewJavaScriptBridge OC 調用 JS 修改顏色.gif](https://upload-images.jianshu.io/upload_images/2701794-532d0eb47bafad35.gif?imageMogr2/auto-orient/strip) ](https://upload-images.jianshu.io/upload_images/2701794-4ed2d1c7b9e867cc.gif?imageMogr2/auto-orient/strip)

首先,在 OC 中,通過 WebViewJavascriptBridge 注冊一個修改 navigationBar 顏色的 Block。

[_jsBridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {
       self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1.0];
        
        responseCallback(@"顏色修改完畢!");
    }];

然后再 JS 中,通過某種方式去調用這個 OC 的 block。

WebViewJavascriptBridge.callHandler('colorClick',function(dataFromOC) {
            alert("JS 調用了 OC 注冊的 colorClick 方法");
            document.getElementById("returnValue").value = dataFromOC;
        })

這里通過某種方式就是使用 WebViewJavascriptBridge.callHandler('OC 中block 別名',callback)的方式來調用。

OC -> JS 的交互

OC 上有一個UIButton,點擊這兒按鈕,把 HTML body 的顏色修改成橙色。

首先,往橋梁中,注入一個修改 HTML body 顏色的 JSFunction。

// 在這里聲明 OC 需要主動調用 JS 的方法。
    setupWebViewJavascriptBridge(function(bridge) {
        bridge.registerHandler('changeBGColor',function(data,responseCallback){
            // alert('aaaaaa');
            document.body.style.backgroundColor = "orange";
            document.getElementById("returnValue").value = data;
        });
    }); 

然后在 OC 端通過橋梁調用這個 changeBGColor

 [_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景顏色改成橙色!!!!"];

執行效果:

WebViewJavaScriptBridge OC 調用 JS 修改顏色.gif

補充

OC 調用 JS 的三種情況。

    // 單純的調用 JSFunction,不往 JS 傳遞參數,也不需要 JSFunction 的返回值。
    [_jsBridge callHandler:@"changeBGColor"];
    // 調用 JSFunction,并向 JS 傳遞參數,但不需要 JSFunciton 的返回值。
    [_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景顏色改成橙色!!!!"];
    // 調用 JSFunction ,并向 JS 傳遞參數,也需要 JSFunction 的返回值。
    [_jsBridge callHandler:@"changeBGColor" data:@"傳遞給 JS 的參數" responseCallback:^(id responseData) {
        NSLog(@"JS 的返回值: %@",responseData);
    }];

JS 調用 OC 的三種情況。

// JS 單純的調用 OC 的 block
WebViewJavascriptBridge.callHandler('scanClick');

// JS 調用 OC 的 block,并傳遞 JS 參數
WebViewJavascriptBridge.callHandler('scanClick',"JS 參數");

// JS 調用 OC 的 block,傳遞 JS 參數,并接受 OC 的返回值。
WebViewJavascriptBridge.callHandler('scanClick',{data : "這是 JS 傳遞到 OC 的掃描數據"},function(dataFromOC){
            alert("JS 調用了 OC 的掃描方法!");
            document.getElementById("returnValue").value = dataFromOC;
        });

可以根據實際情況,選擇合適的方法。

關于在 OC 中,往橋梁中注入 block 的注意點。

在當前控制器消失的時候,要記得把注入到橋梁中的 OC block,從橋梁中刪除。
否則,可能會出現控制器無法釋放的情況。

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [_jsBridge removeHandler:@"scanClick"];
    [_jsBridge removeHandler:@"colorClick"];
    [_jsBridge removeHandler:@"locationClick"];
    [_jsBridge removeHandler:@"shareClick"];
    [_jsBridge removeHandler:@"payClick"];
    [_jsBridge removeHandler:@"goBackClick"];
}


最后總結:

  1. UIWebView & JavaScriptCore 等于原生的 JS & OC 交互方案。
  2. WKWebView & userContentController 等于原生了 JS & OC 交互方案。
  3. WebViewJavascriptBridge 可以搭配 UIWebView & WKWebView 進行 OC & JS 交互。
  4. WebViewJavascriptBridge 使用核心,OC 注入 OC 的方法,讓 JS 調用。JS 注入 JS 函數,讓 OC 調用。
  5. WebViewJavaScriptBridge 使用的需要 4個前提步驟。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容