1、iOS系统API交互方式
1.1、Objective-C调用Javascript方法
前提:
UIWebView已经完全加载完成包含需要调用的页面(注:在UIWebViewDelegate托管中通过监视- (void)webViewDidFinishLoad:(UIWebView *)webView调用来确定页面是否加载完成)。
方法:
假设某视图对象的子视图属性self.webview加载的页面包含如下Javascript函数:
function getString(){ return “Hello javascript!”;}
并且该函数在该页面上可被正常调用,则可以通过形如下面的Objectvie-C方法调用此函数:
NSString *str = [self.webview stringByEvaluatingJavaScriptFromString:@“getString()"];
该函数调用实际模拟了页面上的一次Javascript函数调用,因此在该函数内部任何有效的Javascript代码都可被执行!该Objective-C代码返回值为被调用的Javascript代码的返回值,在上例中,str的值将被赋为@”Hello javascript!”。
被调用的函数可以带有字符串或数值型的参数。若调用的Javascript函数名称带有参数,需保证传进去的参数的格式正确性,特别是字符串参数的引号很容易被忽略。
1.2、客户端响应页面Javascript代码
前提:
需要实现UIWebViewDelegate的- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType托管函数(以下简称跳转监视函数)并将实现了该托管函数的对象设置为UIWebView的delegate。
方法:
UIWebView的当前页面将要被Javascript代码通过以下方式转向的时候,会调用上述跳转监视函数:
window.location.href=”http://www.strongsoft.net”;
此时实现了该托管协议的对象的跳转监视函数会被调用,通过如下代码监视浏览器跳转的地址:
NSString *url = [[request URL] absoluteString];
若该托管函数返回值为NO,则页面UIWebView的页面跳转将被否决。利用这一思路,监视页面上的特定格式的跳转地址加以拦截,并执行相应的本地代码,即可实现Javascript与Objective-C代码的交互。
例如,规定拦截URL地址前缀为”objc:”的所有地址,并且用if…else…语句判定本地需要执行的代码,比如执行doFunc1函数,那么可通过如下Javascript函数进行:
window.location.href=”objc:doFunc1”;
此方式实现JS向客户端传递的消息存在于href的字符串中,客户端通过解析字符串提取所需信息。
2、引入第三方库WebViewJavascriptBridge进行交互
WebViewJavascriptBridge优雅地实现了在使用 UIWebView 时 JS 与 iOS 的 OC 之间的互调,支持消息发送、接收、消息处理器的注册与调用以及设置消息处理的回调。 就像项目的名称一样,它是连接UIWebView和Javascript的bridge。在加入这个项目之后,他们之间的交互处理方式变得很友好。
一起来看看它的实现吧,它总共就包含了三个文件:
1 WebViewJavascriptBridge.h
2 WebViewJavascriptBridge.m
3 WebViewJavascriptBridge.js.txt
WebViewJavascriptBridge.js.txt 主要用于衔接 UIWebView 中的 web page ,而 WebViewJavascriptBridge.h/m 则主要用于与 OC 的 native code 打交道。他们作为一个整体,其实起到了一个“桥梁”的作用,这三个文件封装了他们具体的交互处理方式,只开放出一些对外的涉及到业务处理的API,因此你在需要UIWebView与Native code交互的时候,引入该库,则无需考虑太多的交互上的问题。
出于表达上的需要,对于UIWebView相关的我就称之为Web端,而objc那端的处理代码称之为Native端。
Web端以及Native端完全是对等的两端,实现也是对等的。一段是消息的发送端,另一段就是接收端。这里为引起混淆,需要解释一下我这里使用的“响应”、“回调”在这个上下文中的定义:
(1) 响应:接收端给予发送端的应答
(2) 回调:发送端收到接收端的应答之后在接收端调用的处理逻辑
下面罗列一下它可以实现的功能(Web端):
- Web端在初始化时支持设置消息的默认处理器(这里的消息指的是从Native端接收到的消息)
- 从Web端向Native端发送消息,并支持对于Native端响应后的回调处理的定义
- Web端调用Native定义的处理器,并支持Native端响应后的回调处理定义
- Web端注册处理器(供Native端调用),并支持给Native端响应处理逻辑的定义
//给WebViewJavascriptBridgeReady事件注册一个Listener
<span style="white-space:pre"> </span>
document.addEventListener('WebViewJavascriptBridgeReady', onBridgeReady, false)
//事件的响应处理
<span style="white-space:pre"> </span>
function onBridgeReady(event) {
var bridge = event.bridge
//初始化操作,并定义默认的消息处理逻辑
<span style="white-space:pre"> </span>
bridge.init(function(message) {
log('JS got a message', message)
})
//注册一个名为testJavascriptHandler的处理器,并定义用于响应的处理逻辑(该处理器可在native端调用名为testJavascriptHandler的处理器后,进行响应操作)
<span style="white-space:pre"> </span>
bridge.registerHandler('testJavascriptHandler', function(data, response) {
log('JS handler testJavascriptHandler was called', data)
response.respondWith({ 'Javascript Says':'Right back atcha!' })
})
//创建一个发送消息给native端的按钮
<span style="white-space:pre"> </span>
var button = document.getElementById('buttons').appendChild(document.createElement('button'))
button.innerHTML = 'Send message to ObjC'
button.ontouchstart = function(e) {
e.preventDefault()
<span style="white-space:pre"> </span>//发送消息
bridge.send('Hello from JS button')
}
document.body.appendChild(document.createElement('br'))
//创建一个用于调用native端处理器的按钮
<span style="white-space:pre"> </span>
var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
callbackButton.innerHTML = 'Fire testObjcCallback'
callbackButton.ontouchstart = function(e) {
e.preventDefault()
log("Calling handler testObjcCallback”)
//调用名为testObjcCallback的native端处理器,并传递参数,同时设置回调处理逻辑(与上面对应,客户端也需register一个同样名称的处理器来进行相应响应)
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('Got response from testObjcCallback', response)
})
}
}
3、iOS 8以上系统的交互
iOS 8之后引入了WKWebView,客户端有系统API来处理Web端发送过来的消息,Web端需要做的也精简不少,代码如下:
window.webkit.messageHandlers.testObjcCallbackIniOS8.postMessage({
"infomation": "someData","
});
其中,testObjcCallbackIniOS8是Web端调用Native端的处理器的名称,Native端可在相应方法中,进行判断并处理。
五、参考资料
- http://zh.5long.me/2015/ios-objective-c-javascript-bridge/ 《iOS开发之Objective-C(Swift)与JavaScript交互·WebViewJavascriptBridge使用篇》
- http://www.cnblogs.com/YouXianMing/p/3738317.html 《使用WebViewJavascriptBridge与UIWebView交互》
- http://blog.csdn.net/yanghua_kobe/article/details/8209751 《优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案》