相信在开发的过程中很多页面已经开始慢慢的采用html5来处理了,数据的维护、管理;页面的布局、样式也开始慢慢的被h5渗透,这里不讨论到底开发是h5 app好还是native app 更好,这边对h5与OC的交互做下讲解,存在的就是合理的。
demo已经封装好,下载地址:https://github.com/pengjinguang521/webAndJSDemo.git
一、拦截http请求来处理交互事件
1、javascript的代码 这里面点击按钮时候掉用了打开网页的方法window.location.href="haleyAction://shareClick?title=text&content=content&url=http://www.baidu.com";
function shareClick()
{window.location.href="haleyAction://shareClick?title=text&content=content&url=http://www.baidu.com";
}
2、JS掉起iOS方法,在iOS端的代码使用了web的代理方法当webview加载页面的时候去截获到http的请求,拿到url,然后从url中获取我们所要的信息,或者获取我们所要做的事情
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSURL *URL = request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"haleyaction"]) {
[self handleCustomAction:URL];
return NO;
}
return YES;
}
3、iOS掉起JS的方法,这边主要是使用web的- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;方法来实现的,包括传值,以及h5数据的返回,不过需要注意的是这边返回的是string类型
// 这边是js中的代码
function setLocation(location) {
document.getElementById("text1").value = location;
}
// 这边是ios中的代码
[self.webview stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setLocation(%@);",@"123"]]
二、iOS 7.0以后的
1、开发的时候你只需要导入上面的头文件即可
2、h5中的js代码,这边主要是三个方法:
1. ios掉起js的方法并获取返回值。
2.向js传入参数
3.点击按钮后传值出来
function postStr(){
return document.getElementById("text1").value;
}
function postValue(value){
document.getElementById("text1").value = value;
}
function buttonClick()
{
textlog("hello world");
}
3、客户端的代码实现主要是用JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];context[@“textlog”]的方法,并拿到这个方法内的所有内容。
4、关于该方法掉起js的话,我觉得还是使用web的- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;方法来实现的,包括传值,以及h5数据的返回,不过需要注意的是这边返回的是string类型
三、WebViewJavascriptBridge框架
1、http://www.huangyibiao.com/archives/670 参考网址,这边主要从该博客习得
2、原理:我们看看WebViewJavascriptBridge.m中Webview代理拦截的代码:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (webView != _webView) { return YES; }
NSURL *url = [request URL];
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
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];
}
return NO;
} else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
} else {
return YES;
}
}
在拦截后,通过先通过-isBridgeLoadedURL:方法判断URL是否是需要bridge的URL,若是,则通过injectJavascriptFile方法注入JS;否则判断URL是否是队列消息,若是,则执行查询命令JS并刷新消息队列;最后,URL被识别为未知的消息。
3、在JS端,嵌入步骤是:
第一步:将下面的代码放在JS中:
/*这段代码是固定的,必须要放到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)
}
第二步:在下面的方法体里写相关JS代码:
setupWebViewJavascriptBridge(function(bridge) {
/* Initialize your app here */
所有与iOS交互的JS代码放这里!
})
下面是demo中放入的所有ios与js交互的代码,详见demo中index.html文件
var uniqueId = 1 function log(message, data) { var log = document.getElementById('log') var el = document.createElement('div') el.className = 'logLine' el.innerHTML = uniqueId++ + '. ' + message + ':
' + 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() { 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) }) document.getElementById('blogId').onclick = function (e) { log('js call objc: getBlogNameFromObjC') bridge.callHandler('getBlogNameFromObjC', {'blogURL': 'http://www.henishuo.com'}, function(response) { log('JS got response', response) }) }
4、在iOS端掉用如下:
/** JS掉起oc方法 */
[web.bridge registerHandler:@"getBlogNameFromObjC" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"%@_____data",data);
if (responseCallback) {
responseCallback(@{@"blogName": @"标哥的技术博客"});
}
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
/** OC掉起js方法 */
[web.bridge callHandler:@"getUserInfos" data:@{@"name": @"标哥"} responseCallback:^(id responseData) {
NSLog(@"from js: %@", responseData);
}];
});