iOS WebView与JS交互

最近在做的项目重点就是原生app与js的交互,以前也做过但是并没有深入的了解和研究过,因为这个项目我尝试了三种方式的交互方法,经过实践也更了解每种方法的利弊,再次分享一下希望大家可以批评指正:

首先分析一下app目前的样式分类原生app和webapp的定义以及优缺点Web App

WebApp

即是一种框架型APP开发模式(HTML5 APP框架开发模式),该开发具有跨平台的优势,该模式通常由“HTML5云网站+APP应用客户端”两部份构成,APP应用客户端只需安装应用的框架部份,而应用的数据则是每次打开APP的时候,去云端取数据呈现给手机用户。

原生App

原生APP又称Native

App,该开发针对IOS、Android、Windows等不同的手机操作系统要采用不同的语言和框架进行开发,该模式通常是由“云服务器数据+APP应用客户端”两部份构成,APP应用所有的UI元素、数据内容、逻辑框架均安装在手机终端上。

Hybrid App(混合模式移动应用)

是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。

这篇文章作者总结的不错

WebAPP与原生APP的交互设计区别 - 简书

http://www.oschina.net/question/585173_2141646

这篇文章讲述了目前移动开发的趋势:

为什么移动开发开始用混合app开发(Hybrid App)

知道了各种形式app的定义却别,也更了解Hybrid App的优势那怎样更好的实现原生与web 的交互呢?

首先我认为最好的方式

1. 运用JavaScriptCore框架进行交互

在iOS 7之后,apple添加了一个新的库JavaScriptCore,用来做JS交互,因此JS与原生OC交互也变得简单了许多。这是我做的一个例子

1.首先创建JSContext 对象(此处通过当前webView的键获取到jscontext)

_mJSContext=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

2.异常处理

_mJSContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {

context.exception = exceptionValue;

DLog(@"%@", exceptionValue);

};

3.在JS中添加了方法指针提供给JS调用

@WeakObj(self);

_mJSContext[@"jSInvokerGetMid"] = ^(void){

DLog(@"JS获取UUID");

@StrongObj(self);

return  self.devie.mac;

};

_mJSContext[@"jSInvokerGetLocation"] = ^(void){

DLog(@"JS获取坐标");

@StrongObj(self);

id devicePoint = @{

@"latitude": [NSString stringWithFormat:@"%f", self.location.coordinate.latitude],

@"longitude": [NSString stringWithFormat:@"%f", self.location.coordinate.longitude]

};

return devicePoint;

};

_mJSContext[@"jSInvokerGetUserSgid"] = ^(void){

DLog(@"JS用户的ID");

return @"UserId";

};

_mJSContext[@"jSInvokerSpeechSubView"] = ^(void){

DLog(@"将要进行界面跳转由h5界面跳转到原生界面");

@StrongObj(self);

NSArray *args = [JSContext currentArguments];

JSValue * value = args[0];

NSString * str = value.toString;

//必须放入主线程中更新UI否则会出错

dispatch_async(dispatch_get_main_queue(), ^{

SGHomeSeachDetailController * detailViewCon = [[SGHomeSeachDetailController alloc]init];

detailViewCon.baseUrl = str;

[self.navigationController pushViewController: detailViewCon animated:YES];

});

};

4.iOS调用JS进行初始化

NSString *textJS = @"window.ios_callback('这里是JS中alert弹出的message')";

[_mJSContext evaluateScript:textJS];

也可以参考:IOS中 使用JavaScriptCore 实现OC与JS的交互 - 简书

以及我的博客:JS和Native交互之 - 运用JavaScriptCore框架进行交互 - lxm_780337的博客         - 博客频道 - CSDN.NET

WebViewJavascriptBridge

Obj-C和JavaScript原理:

Obj-C调用JavaScript很简单,可以通过webview的stringByEvaluatingJavaScriptFromString:方法调用JavaScript代码;JavaScript调用Obj-C,则是通过web

view的代理方法shouldStartLoadWithRequest:来接收JavaScript的网络请求从而实现调用。

利弊:

需要把响应的方法放到桥梁内,这样对安卓端可能会中影响

不错的例子:

1.按照本地的CSS文件展示一串网络获取的带HTML格式的只有body部分的文本,需要自己拼写完整的HTML。除此之外,还需要禁用获取的HTML文本中自带的

《 img 》

标签自动加载,并把下载图片的操作放在native端来处理,并通过JS将图片在Cache中的地址返回给UIWebview。http://www.cocoachina.com/ios/20150814/12985.html

过程:

一开始,我们在Native端和JS端都分别进行初始化:

OC端:

@property WebViewJavascriptBridge* bridge;

对应的初始化代码如下,在初始化中直接包含了一个用于接收JS的回调:

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@"ObjC received message from JS: %@", data);

responseCallback(@"Response for message from ObjC");

}];

JS端:(以下是固定写法,你自己的JS文件中必须包含如下代码)

function connectWebViewJavascriptBridge(callback) {

if (window.WebViewJavascriptBridge) {

callback(WebViewJavascriptBridge)

} else {

document.addEventListener('WebViewJavascriptBridgeReady', function() {

callback(WebViewJavascriptBridge)

}, false)

}

connectWebViewJavascriptBridge(function(bridge) {

//注意,你的bridge函数都应写在这里面

}

//例如

function doSomething(){

connectWebViewJavascriptBridge(function(bridge) {

bridge.init(function(message, yourCallback) {

log('ObjC called testJavascriptHandler with', message)

var responseData = { 'Javascript Says':'Right back atcha!' }

log('JS responding with', responseData)

responseCallback(responseData)

})

}

}

然后,我们要知道,在WebViewJavascriptBridge中,交互的方式只有两种:send 和 callHandle,JS和OC都有这两个方法,所以对应的四种关系是:

blob.png

OC调用JS方法

1.调用JS中bridge.init的方法

//OC

[_bridge send:@"The message sent from ObjC to JS" responseCallback:^(id response) {

NSLog(@"sendMessage got response: %@", response);//这里的response是js方法中的data

}];

//对应调用js中的方法

bridge.init(function(message, yourCallback) {

log('ObjC called testJavascriptHandler with', message)

var responseData = { 'Javascript Says':'Right back atcha!' }

log('JS responding with', responseData)

responseCallback(responseData)

})

2.调用JS中bridge.registerHandler方法,该方法可以注册方法名,通过注册名确认调用方法

//OC

[_bridge callHandler:@"注册名" data:data responseCallback:^(id response) {

NSLog(@"testJavascriptHandler responded: %@", response);

}];

//对应调用的js中的方法

bridge.registerHandler('注册名', function(data, responseCallback) {

log('ObjC called testJavascriptHandler with', data)

var responseData = { 'Javascript Says':'Right back atcha!' }

log('JS responding with', responseData)

responseCallback(responseData)

})

JS端调用OC端方法

1.调用OC中创建bridge对象时定义时的方法

//JS中

bridge.send(data,function(response){

log('这里是回调方法',response) //回调方法

})

//对应调用的OC中bridge初始化中设置的block方法

_bridge

= [WebViewJavascriptBridge bridgeForWebView:webView

webViewDelegate:self handler:^(id data, WVJBResponseCallback

responseCallback) {

//do something...

NSLog(@"ObjC received message from JS: %@", data);//从js获得的参数

responseCallback(@"Response");//Response to js

}];

2.通过方法名调用OC中bridge的注册方法

//JS中

bridge.callHandler('注册名', {'foo': 'bar'}, function(response) {

log('JS got response', response)

})

//对应调用的OC中的bridge注册的方法

[_bridge registerHandler:@"注册名" handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@"testObjcCallback called: %@", data);//从js获取的参数

responseCallback(@"Response");//

}];

这篇文章讲了交互原理非常不错:JS和Native交互之 -WebViewJavascriptBridge源码分析 - lxm_780337的博客         - 博客频道 - CSDN.NET

UIWebView的代理方法

原理:- (BOOL)webView:(UIWebView *)webView

shouldStartLoadWithRequest:(NSURLRequest *)request

navigationType:(UIWebViewNavigationType)navigationType;在WebView中的wap页将要载入内容时得到通知触发,返回NO则阻止加载内容,优点:简单易用,在页面跳转请求时进行iOS端的页面跳转,并返回NO阻止wap内的页面跳转缺点:无法识别不加载新内容的JS动作(点击按钮弹出对话框和提交等)例子:HTML网页和一个btn点击事件用来与原生OC交互(用JS发起一个假的URL请求,然后利用UIWebView的代理方法拦截这次请求,然后再做相应的处理)JS调用IOSHTML代码如下:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容