UIWebView中JS与OC交互 WebViewJavascriptBridge的使用
Posted 孙富有(iOS工程师)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UIWebView中JS与OC交互 WebViewJavascriptBridge的使用相关的知识,希望对你有一定的参考价值。
一、综述
现在很多的应用都会在多种平台上发布,所以很多程序猿们都开始使用Hybrid App的设计模式。就是在app上嵌入网页,只要写一份网页代码,就可以跑在不同的系统上。在ios中,app多是通过WebView来加载网页,由于功能需求等原因,代码中少不得要和跟网页交互。
二、原理
在iOS中,本地调用javascript语言,是通过UIWebView中的实例方法stringByEvaluatingJavaScriptFromString:来实现的,该方法通过字符串对象的形式传入JS代码。
1
|
[webView stringByEvaluatingJavaScriptFromString:@ "Math.random();" ]; |
而JS调用本地的代码,则并没有现成的API,而是需要间接地通过一些方法来实现。我们利用UIWebView的代理方法,当UIWebView发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView内发起一个自定义的网络请求,比如:‘wvjbscheme://__BRIDGE_LOADED__‘。于是在UIWebView的delegate函数中,我们拦截url,只要发现是我们自定义的url,就不进行内容的加载,转而执行相应的调用逻辑。
三、WebViewJavascriptBridge的使用
1、WebViewJavascriptBridge简介
WebViewJavascriptBridge支持到iOS6之前的版本的,用于支持native的iOS与javascript交互,接下来讲讲WebViewJavascriptBridge的基本原理及应该如何去使用,包括iOS端的使用和JS端的使用。
首先,看看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被识别为未知的消息。
2、WebViewJavascriptBridge的使用
首先,要在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调用
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‘ 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); }]; |
四、小结
最近因为项目需要,正在边学边做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
以上是关于UIWebView中JS与OC交互 WebViewJavascriptBridge的使用的主要内容,如果未能解决你的问题,请参考以下文章
iOS下JS与OC互相调用--WKWebView 拦截URL
js(javascript)与OC(Objective-C)交互