一、簡介
在前面兩篇文章中已經介紹了Native與JavaScript交互的幾種方式,依次是JavaScriptCore框架、UI組件UIWebView、WebKit框架,這幾種方式都是蘋果公司提供的,除了UIWebView在IOS8之后被蘋果淘汰了外,其他基本都能很好地滿足開發者的使用。作為一個技術人員,每個人心里都有造輪子的想法,可能有時覺得原生的API使用起來感覺還是不夠方便,就對蘋果原生的API再進行一層封裝,這不WebViewJavascriptBridge這個輪子出來了。WebViewJavascriptBridge的star和fork量還是比較高的,仔細看看WebViewJavascriptBridge類文件相當簡單,使用起來也很方便,很受開發者歡迎,它的原理還是利用WKWebView或者UIWebView的相關API,通過bridge橋梁來實現OC與JS互相注冊和調用。大致結構圖如下:
可以看出:OC調用JS,JS需要注冊函數; JS調用OC,OC需要注冊函數。
二、分析
了解基本原理結構圖后,再來看看框架中的類以及它們的作用定義,如下:
WebViewJavascriptBridge_JS:Javascript環境的Bridge初始化和處理。負責接收OC發給Javascript的消息,並且把Javascript環境的消息發送給OC。
WKWebViewJavascriptBridge/WebViewJavascriptBridge:主要負責OC環境的消息處理,並且把OC環境的消息發送給Javascript環境。
WebViewJavascriptBridgeBase:主要實現了OC環境的Bridge初始化和處理。
//初始化橋接器 + (instancetype)bridgeForWebView:(id)webView; + (instancetype)bridge:(id)webView; //設置日志相關 + (void)enableLogging; + (void)setLogMaxLength:(int)length; //注冊函數, handlerName: 函數名稱 handler:傳遞數據的block - (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; - (void)removeHandler:(NSString*)handlerName; //調用函數, handlerName:函數名稱 data:參數 responseCallback:接收數據的block - (void)callHandler:(NSString*)handlerName; - (void)callHandler:(NSString*)handlerName data:(id)data; - (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; //設置網頁代理 - (void)setWebViewDelegate:(id)webViewDelegate; //禁用超時安全彈框 - (void)disableJavscriptAlertBoxSafetyTimeout;
調用相關知識點 :
// OC 調用 JS
// 1、單純的調用 JSFunction。 [self.jsBridge callHandler:@"JSFunction"]; // 2、調用 JSFunction,並傳遞給js需要的參數,但不需要 JSFunciton 的返回值。 [self.jsBridge callHandler:@"JSFunction" data:"arg of js"]; // 3、調用 JSFunction ,並傳遞給js需要的參數,也需要 JSFunction 的返回值。 [self.jsBridge callHandler:@"JSFunction" data:"arg of js" responseCallback:^(id responseData) { NSLog(@"%@",responseData); }]; // ------------------------------------------------------------------------------- // //JS 調用 OC // 1、JS 單純的調用 OC 的 OCMethod WebViewJavascriptBridge.callHandler('OCMethod'); // 2、JS 調用 OC 的 OCMethod,並傳遞給OC需要的參數 WebViewJavascriptBridge.callHandler('OCMethod',"arg of oc"); // 3、JS 調用 OC 的 OCMethod,傳遞給OC需要的參數,並接受OC的返回值。 WebViewJavascriptBridge.callHandler('OCMethod', "arg of oc", function(responseValue){ alert(responseValue);
});
三、核心
使用該框架,還需要完成某些初始化的工作,也即在HTML或者JavaScript文件中,拷貝進官方指定的函數,在函數內進行初始化操作:
//固定格式的函數 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) } //官方說這里主要進行初始化,有兩個功能:1、進行JS函數的注冊提供OC調用(必須在此實現) 2、JS調用OC在端上注冊的函數(不用在此處實現) //這里需要說明一下: 至於第2點如果放在這里會立即執行且執行一次。可以挪到某一個js事件中執行,例如按鈕事件等,可以頻繁觸發,后面的使用會有demo演示 setupWebViewJavascriptBridge(function(bridge) { /* Initialize your app here */ bridge.registerHandler('JavaScript_functionName', function(data, responseCallback) { responseCallback(data); //傳遞數據給OC }) bridge.callHandler('OC_methodName', function responseCallback(responseData) { alert(responseData); //接收OC的數據 }) })
四、使用
Example introduce: 頁面有一個原生按鈕和H5按鈕,點擊原生按鈕調用JS,切換H5的div背景色;點擊H5按鈕,調用OC方法,切換原生按鈕背景顏色。
完整代碼:

// // ViewController.m // WebViewJavaScriptBridge // // Created by 夏遠全 on 2019/11/17. // Copyright © 2019 Beijing Huayue Education Technology Co., Ltd. All rights reserved. // #import "ViewController.h" #import "WebViewJavascriptBridge/WebViewJavascriptBridge.h" @interface ViewController () @property (nonatomic, strong) WebViewJavascriptBridge *bridge; @property (nonatomic, strong) UIButton *topButton; @property (nonatomic, strong) WKWebView *wkWebView; @end @implementation ViewController #pragma mark - init - (void)viewDidLoad { [super viewDidLoad]; //創建topView CGFloat width = [UIScreen mainScreen].bounds.size.width; self.topButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, width, 200)]; self.topButton.titleLabel.numberOfLines = 0; self.topButton.backgroundColor = [UIColor blueColor]; self.topButton.titleLabel.font = [UIFont systemFontOfSize:17]; [self.topButton setTitle:@"我是TopButton\n點擊我調用JS方法,切換div盒子的背景色" forState:UIControlStateNormal]; [self.topButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [self.topButton addTarget:self action:@selector(topButtonAction) forControlEvents:UIControlEventTouchUpInside]; //創建wkWebView CGFloat height = [UIScreen mainScreen].bounds.size.height-CGRectGetHeight(self.topButton.frame); self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.topButton.frame), width, height)]; //添加wkWebView視圖 [self.view addSubview:self.topButton]; [self.view addSubview:self.wkWebView]; //為wkWebView創建橋接器 self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wkWebView]; //OC注冊方法,提供給JavaScript調用,並給JavaScript傳遞數據 [self.bridge registerHandler:@"updateTopButtonBgColor" handler:^(id data, WVJBResponseCallback responseCallback) { UIColor *randomColor = [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1.0]; self.topButton.backgroundColor = randomColor; [self showAlertView:@"JavaScript調用OC ----- success! "]; responseCallback(@"JavaScript調用OC ----- success! "); //可以回調給JavaScript一個結果 }]; //加載資源 NSString *file = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"html"]; NSString *html = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil]; [self.wkWebView loadHTMLString:html baseURL:nil]; } #pragma mark - remove - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.bridge removeHandler:@"updateTopButtonBgColor"]; } #pragma mark -action -(void)topButtonAction { //調用JavaScript函數,傳遞顏色參數,並接收JavaScript回傳的數據 NSArray *colors = [NSArray arrayWithObjects:@"orange",@"green",@"red",@"blue",nil]; [self.bridge callHandler:@"updateDivBgColor" data:colors[arc4random_uniform(4)] responseCallback:^(id responseData) { [self showAlertView:responseData]; }]; } #pragma mark - method -(void)showAlertView:(NSString *)message { UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *action = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; [aletVc addAction:action]; [self presentViewController:aletVc animated:YES completion:nil]; } @end
細分步驟:
1、創建HTML,完成初始工作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .divcss{ background:#F00; color:#FFF; width:300px; height:200px} </style> <script type="text/javascript"> function button_onclick(){ //JavaScript調用OC注冊的方法,並接收OC的數據 WebViewJavascriptBridge.callHandler('updateTopButtonBgColor',function(responseValue) { alert(responseValue); }); } // 固定函數 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) } // 初始化 setupWebViewJavascriptBridge(function(bridge){ //JavaScript調用OC注冊的方法,並接收OC的數據,頁面啟動后會立即調用且調用一次,我把它放到了上面的👆button_onclick()事件中可以頻繁執行 //WebViewJavascriptBridge.callHandler('changeTopButtonBgColor',function(responseValue) { //alert(responseValue); //}); //JavaScript注冊函數,提供給OC調用,並傳遞數據給OC bridge.registerHandler('updateDivBgColor',function(colorData, responseCallback) { alert(colorData); document.getElementById("div").style.backgroundColor = colorData; responseCallback("OC調用JavaScript ----- success! "); }); }); </script> </head> <body> <div class="divcss" id="div"> <br> <h2 style="text-align:center"> div盒子 </h2> </div> <br> <button onclick="button_onclick()">點擊調用OC方法,切換TopButton的背景色</button> </body> </html>
2、導入頭文件,定義控件屬性
//頭文件 #import "ViewController.h" #import "WebViewJavascriptBridge/WebViewJavascriptBridge.h" //定義屬性 @interface ViewController () @property (nonatomic, strong) WebViewJavascriptBridge *bridge; @property (nonatomic, strong) UIButton *topButton; @property (nonatomic, strong) WKWebView *wkWebView; @end
3、創建控件,並添加到父視圖
//創建topView CGFloat width = [UIScreen mainScreen].bounds.size.width; self.topButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, width, 200)]; self.topButton.titleLabel.numberOfLines = 0; self.topButton.backgroundColor = [UIColor blueColor]; self.topButton.titleLabel.font = [UIFont systemFontOfSize:17]; [self.topButton setTitle:@"我是TopButton\n點擊我調用JS方法,切換div盒子的背景色" forState:UIControlStateNormal]; [self.topButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [self.topButton addTarget:self action:@selector(topButton) forControlEvents:UIControlEventTouchUpInside]; //創建wkWebView CGFloat height = [UIScreen mainScreen].bounds.size.height-CGRectGetHeight(self.topButton.frame); self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.topButton.frame), width, height)]; //添加wkWebView視圖 [self.view addSubview:self.topButton]; [self.view addSubview:self.wkWebView];
4、創建橋接器
//為wkWebView創建橋接器 self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wkWebView];
5、JavaScript調用OC: 需要OC注冊方法
//OC注冊方法,提供給JavaScript調用,並給JavaScript傳遞數據 [self.bridge registerHandler:@"changeTopButtonBgColor" handler:^(id data, WVJBResponseCallback responseCallback) { UIColor *randomColor = [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1.0]; self.topButton.backgroundColor = randomColor; [self showAlertView:@"JavaScript調用OC ----- success! "]; responseCallback(@"JavaScript調用OC ----- success! "); //可以回調給JavaScript一個結果 }];
6、OC調用JavaScript:需要在HTML中注冊JS函數
//原生按鈕事件 -(void)topButtonAction { //調用JavaScript函數,傳遞顏色參數,並接收JavaScript回傳的數據 NSArray *colors = [NSArray arrayWithObjects:@"orange",@"green",@"red",@"blue",nil]; [self.bridge callHandler:@"updateDivBgColor" data:colors[arc4random_uniform(4)] responseCallback:^(id responseData) { [self showAlertView:responseData]; }]; }
7、加載HTML資源
//加載資源 NSString *file = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"html"]; NSString *html = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil]; [self.wkWebView loadHTMLString:html baseURL:nil];
8、清除工作
//需要清除注冊到橋接器中的函數,否則導致vc無法釋放 - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.bridge removeHandler:@"updateTopButtonBgColor"]; }
9、顯示結果如下:可以發現TopButton和div的背景色都能頻繁改變