iOS之OC与JS交互(WebViewJavascriptBri
前言
最近项目开发中用到了OC与JS交互方面的知识,以前也用过UIWebView JS与OC交互方面的,使用的苹果在iOS7开放的javascriptCore框架,使用起来挺方便快捷,javascriptCore源码是开放的,有兴趣的可以去了解一下。
自从iOS8,苹果就UIWebView性能不好,推出了WKWebView,以及github上评分很高的WebViewJavascriptBridge里面最新版本也对WKWebView做了兼容。
实现的方式
所以我总结的方式,分UIWebview、WKWebView、以及通用版本的第三方WebViewJavascriptBridge,进行实现。
- UIWebView中JS与OC交互
- WKWebView中JS与OC交互(只能iOS8及之后的版本)
- WebViewJavascriptBridge对UIWebView与WKWebView做了统一处理。
WebViewJavascriptBridge
优点
-
UIWebView和WKWebView使用基本相同
-
WebViewJavascriptBridge使用起来方便快捷
-
安卓版本和iOS版本WebViewJavascriptBridge,实现的JS与原生交互都是一样的,不用针对两个端分别设置不同的代码
基本原理
-
WebViewJavascriptBridge就相当与沟通OC与JS的桥梁。
-
把 OC 的方法注册到桥梁中,让 JS 去调用。
-
把 JS 的方法注册在桥梁中,让 OC 去调用。
就是谁注册方法谁就是等着被调用,如下图:
WebViewJavascriptBridge原理图.png使用步骤
- 项目中导入 WebViewJavaScriptBridge 框架
pod ‘WebViewJavascriptBridge’
- 导入头文件
#import <WebViewJavascriptBridge.h>
-
初始化WKWebView/UIWebView设置代理
-
要是UIWebView那就设置UIWebViewDelegate
-
要是WKWebView那就设置WKNavigationDelegate、WKUIDelegate
-
代码如下:
//初始化WKWebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
webView.navigationDelegate = self;
webView.UIDelegate = self;
[self.view addSubview:webView];
//或者初始化UIWebView
UIWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
webView.delegate = self;
[self.view addSubview:webView];
- 给webView建立JS与OjbC的沟通桥梁
self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
[self.bridge setWebViewDelegate:self];
- OC 注册两个方法,等着JS调用,responseCallback()是返回js的数据,可不传
//registerHandler 注册,等着JS调用,responseCallback是返回给JS的数据
[self.bridge registerHandler:@"getUserIdFromObjC" handler:^(id data, WVJBResponseCallback responseCallback) {
if (responseCallback) {
responseCallback(@{@"userId":@"123456"});
}
}];
//js调用的这个方也可以返回给JS数据
[self.bridge registerHandler:@"notReturnFromJS" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"data -- %@",data);
}];
- 在click事件添加OnOCCallJSClick事件,调用JS
- (void) OnOCCallJSClick:(id)sender {
//这种调用js方式,不需要js在给返回值
[self.bridge callHandler:@"getInfoFromJS" data:@{@"name":@"张三"}];
//callHandler 调用JS 这个方式是,调用js,js有返回值的话,responseData就是js返回的
[self.bridge callHandler:@"getInfoFromJS" data:@{@"name":@"张三"} responseCallback:^(id responseData) {
}];
}
- 在你需要加载的html中在<script></script>写上统一的代码,这段代码就能使js与oc建立bridge。
<script>
window.onerror = function(err) {
log('window.onerror: ' + err)
}
/*这段代码是固定的,必须要放到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方法都要放在此处注册,才能调用通过JS调用OC或者让OC调用这里的JS*/
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)
}
}
/* Initialize your app here */
/*我们在这注册一个js调用OC的方法,不带参数,
*且不用ObjC端反馈结果给JS:打开本demo对应的博文
*/
bridge.registerHandler('openWebviewBridgeArticle', function() {
confirm("郭杨和小代是好朋友吗?");
log("openWebviewBridgeArticle was called with by ObjC")
})
/*JS给ObjC提供公开的API,在ObjC端可以手动调用JS的这个API。
*接收ObjC传过来的参数,且可以回调ObjC
*/
bridge.registerHandler('getUserInfos', function(data, responseCallback) {
log("Get user information from ObjC: ", data)
responseCallback({'userId': '123456', 'blog': '标哥的技术博客'})
})
/*JS给ObjC提供公开的API,ObjC端通过注册,就可以在JS端调用此API时,得到回调。
*ObjC端可以在处理完成后,反馈给JS,这样写就是在载入页面完成时就先调用
*/
bridge.callHandler('getUserIdFromObjC', function(responseData) {
log("JS call ObjC's getUserIdFromObjC function, and js received response:", responseData)
})
})
</script>
-
在JS中也有registerHandler这个方法,注册方法等着OC来调用
-
在JS中callHandler方法来调用OC方法,
-
JS的responseCallback是OC返回的数据,OC可以不传,responseCallback就为空
注意
如果使用WKWebview来实现WebViewJavascriptBridge,依然存在调用系统的confirm、alert、prompt弹出框被拦截,所以还是要实现UIDelegate代理的三个方法,拦截以后弹出原生的弹出框
#pragma mark - WKUIDelegate
//! alert(message)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
//! confirm(message)
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Confirm" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
[alertController addAction:cancelAction];
[alertController addAction:confirmAction];
[self presentViewController:alertController animated:YES completion:nil];
}
//! prompt(prompt, defaultText)
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = defaultText;
}];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text);
}];
[alertController addAction:confirmAction];
[self presentViewController:alertController animated:YES completion:nil];
}
结尾
WebViewJavascriptBridge使用起来比较方便快捷,并且还有安卓版本的WebViewJavascriptBridge,所有安卓和iOS都使用的话,就避免了前端既要写一份安卓又要写一份iOS的JS与OC交互,调试起来只要一端通了,JS那边应该就没啥问题,排查问题方便,不用的iOS和安卓会有不同的问题,可能你解决iOS会引起安卓问题,所以统一性比较重要。(如些有不足请批评指正,满意的话给个赞~~~)