ios中怎么js调用url 返回值js并拿到返回值

iOS开发日记28-OC与JS的交互(JavaScriptCore)_iOS开发_动态网站制作指南
iOS开发日记28-OC与JS的交互(JavaScriptCore)
来源:人气:2541
今天博主有一个OC与JS的交互的需求,遇到了一些困难点,在此和大家分享,希望能够共同进步.
上一篇博文与大家分享了如何使用webview与JS进行交互,但是随着新的API的开放,我们不仅仅需要在webview中与JS进行交互,也需要在很多其他的框架中与JS进行交互,有两种方法提供给大家:
1.苹果在7.0之后新开放得API&ScrtCore框架,让我们能够更简便的使用OC与JS进行交互.
2.使用第三方框架WebViewBridge
今天就和大家总结一下JavaScriptCore框架的使用.
JavaScriptCore中的类
在项目中引入JavaScriptCore后,链到头文件中,除了大段的Copyright注释可以看到里面只要引入了5个文件,每个文件里都定义跟文件名对应的类:
JSManagedValue
JSVirtualMachine
虽说代码中的注释介绍的也比较详细了,但是如同一图顶万言,对程序员来说代码更有说服力。本文就先来说说这些类相对比较好理解但又很常用的JSContext和JSValue以及它们方法的使用方式和效果。
JSContext和JSValue
JSVirtualMachine为JavaScript的运行提供了底层资源,JSContext就为其提供着运行环境,通过- (JSValue *)evaluateScript:(NSString *)方法就可以执行一段JavaScript脚本,并且如果其中有方法、变量等信息都会被存储在其中以便在需要的时候使用。而JSContext的创建都是基于JSVirtualMachine:- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualM,如果是使用- (id)进行初始化,那么在其内部会自动创建一个新的JSVirtualMachine对象然后调用前边的初始化方法。
JSValue则可以说是JavaScript和Object-C之间互换的桥梁,它提供了多种方法可以方便地把JavaScript数据类型转换成Objective-C,或者是转换过去。其一一对应方式可见下表:
Objective-CJavaScriptJSValue ConvertJSValue Constructor
valueWithUndefinedInContext
valueWithNullInContext:
number, boolean
toNumbertoBooltoDoubletoInt32toUInt32
valueWithBool:inContext:valueWithDouble:inContext:valueWithInt32:inContext:valueWithUInt32:inContext:
NSDictionary
Object object
toDictionary
valueWithNewObjectInContext:
Array object
valueWithNewArrayInContext:
Date object
Function object
Wrapper object
toObjecttoObjectOfClass:
valueWithObject:inContext:
Constructor object
基本类型转换
先看个简单的例子:
JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];
NSLog(@"JSValue: %@, int: %d", jsVal, iVal);
// JSValue: 28, int: 28
很简单吧,还可以存一个JavaScript变量在JSContext中,然后通过下标来获取出来。而对于Array或者Object类型,JSValue也可以通过下标直接取值和赋值。
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"var arr = [21, 7 , ''];"];
JSValue *jsArr = context[@"arr"]; // Get array from JSContext
NSLog(@"JS Array: %@; Length: %@", jsArr, jsArr[@"length"]);
jsArr[1] = @"blog"; // Use JSValue as array
jsArr[7] = @7;
NSLog(@"JS Array: %@; Length: %d", jsArr, [jsArr[@"length"] toInt32]);
NSArray *nsArr = [jsArr toArray];
NSLog(@"NSArray: %@", nsArr);
// JS Array: 21, Length: 3
// JS Array: 21,,,,,,7 Length: 8
// NSArray: (
// "&null&",
// "&null&",
// "&null&",
// "&null&",
通过输出结果很容易看出代码成功把数据从Objective-C赋到了JavaScript数组上,而且JSValue是遵循JavaScript的数组特性:无下标越位,自动延展数组大小。并且通过JSValue还可以获取JavaScript对象上的属性,比如例子中通过"length"就获取到了JavaScript数组的长度。在转成NSArray的时候,所有的信息也都正确转换了过去。
方法的转换
各种数据类型可以转换,Objective-C的Block也可以传入JSContext中当做JavaScript的方法使用。比如在前端开发中常用的log方法,虽然JavaScritpCore没有自带(毕竟不是在网页上运行的,自然不会有window、document、console这些类了),仍然可以定义一个Block方法来调用NSLog来模拟:
JSContext *context = [[JSContext alloc] init];
context[@"log"] = ^() {
NSLog(@"+++++++Begin Log+++++++");
NSArray *args = [JSContext currentArguments];
for (JSValue *jsVal in args) {
NSLog(@"%@", jsVal);
JSValue *this = [JSContext currentThis];
NSLog(@"this: %@",this);
NSLog(@"-------End Log-------");
[context evaluateScript:@"log('ider', [7, 21], { hello:'world', js:100 });"];
// +++++++Begin Log+++++++
// [object Object]
// this: [object GlobalObject]
// -------End Log-------
通过Block成功的在JavaScript调用方法回到了Objective-C,而且依然遵循JavaScript方法的各种特点,比如方法参数不固定。也因为这样,JSContext提供了类方法来获取参数列表(+ (JSContext *)currentC)和当前调用该方法的对象(+ (JSValue *)currentThis)。对于"this",输出的内容是GlobalObject,这也是JSContext对象方法- (JSValue *)globalO所返回的内容。因为我们知道在JavaScript里,所有全局变量和方法其实都是一个全局变量的属性,在浏览器中是window,在JavaScriptCore是什么就不得而知了。
Block可以传入JSContext作方法,但是JSValue没有toBlock方法来把JavaScript方法变成Block在Objetive-C中使用。毕竟Block的参数个数和类型已经返回类型都是固定的。虽然不能把方法提取出来,但是JSValue提供了- (JSValue *)callWithArguments:(NSArray *)方法可以反过来将参数传进去来调用方法。
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"function add(a, b) { return a + }"];
JSValue *add = context[@"add"];
NSLog(@"Func: %@", add);
JSValue *sum = [add callWithArguments:@[@(7), @(21)]];
NSLog(@"Sum: %d",[sum toInt32]);
// Func: function add(a, b) { return a + }
// Sum: 28
JSValue还提供- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)让我们可以直接简单地调用对象上的方法。只是如果定义的方法是全局函数,那么很显然应该在JSContext的globalObject对象上调用该方法;如果是某JavaScript对象上的方法,就应该用相应的JSValue对象调用.
下面贴上代码,我们还是以webview作为示例,各位看官自行深究.
首先我们添加协议
&@interface&ViewController&()&UIWebViewDelegate& &
实现代理方法
#agma&mark&--webViewDelegate &
-(BOOL)webView:(UIWebView&*)webView&shouldStartLoadWithRequest:(NSURLRequest&*)request&navigationType:(UIWebViewNavigationType)navigationType &
return&YES; &
-(void)webViewDidStartLoad:(UIWebView&*)webView&&
-(void)webViewDidFinishLoad:(UIWebView&*)webView&&
-(void)webView:(UIWebView&*)webView&didFailLoadWithError:(NSError&*)error &
每个方法是什么时候调用都在注释里面
1.我们先尝试用oc调用一下js方法
&-(void)webViewDidFinishLoad:(UIWebView&*)webView &
& & & JSContext&*context=[webView&valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; &
& & & NSString&*alertJS=@"alert('test&js&OC')";&
下来我们使用js调用iOS
js调用iOS分两种情况
一,js里面直接调用方法
二,js里面通过对象调用方法
首先我们看第一种,直接调用方法。
其中用到了iOS的block
&-(void)webViewDidFinishLoad:(UIWebView&*)webView &
& & & JSContext&*context=[webView&valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; &
& & & context[@"test1"]&=&^()&{ &
& & & NSArray&*args&=&[JSContext&currentArguments]; &&
& & & for&(id&obj&in&args)&{ &
&&&&&&&&&&&&NSLog(@"%@",obj);&&
&&&&&&&&}&&
& & NSString&*jsFunctStr=@"test1('参数1')"; &
&&&&[context&evaluateScript:jsFunctStr]; &
& & NSString&*jsFunctStr1=@"test1('参数a','参数b')"; &
&&&&[context&evaluateScript:jsFunctStr1];&&
以上代码中 给test1赋值的block要是执行了那么结果就是对的,js调用了iOS
我们看结果
我们看到参数被打印出来了,而且一个参数和两个参数都可以,也符合js参数个数不限制
下来我们看第二种情况 就是js 中是通过一个对象来调用方法的。
此处稍微复杂一点我们需要使用到
凡事添加了JSExport协议的协议,所规定的方法,变量等 就会对js开放,我们可以通过js调用到
此处有点绕。我们直接看代码
首先创建一个类 继承NSObject 并且规定一个协议
& &#import&&Foundation/Foundation.h& &
& &#import&&JavaScriptCore/JavaScriptCore.h& &&
& &@protocol&TestJSObjectProtocol&&JSExport& &
-(void)TestNOP &
-(void)TestOneParameter:(NSString&*)&&
-(void)TestTowParameter:(NSString&*)message1&SecondParameter:(NSString&*)message2; &
//让我们创建的类实现上边的协议 &
@interface&TestJSObject&:&NSObject&TestJSObjectProtocol& &
& & #import&"TestJSObject.h" &&&
& & @implementation&TestJSObject &
-(void)TestNOParameter&&
&&&&NSLog(@"this&is&ios&TestNOParameter");&&
-(void)TestOneParameter:(NSString&*)message&&
&&&&NSLog(@"this&is&ios&TestOneParameter=%@",message);&&
-(void)TestTowParameter:(NSString&*)message1&SecondParameter:(NSString&*)message2&&
&&&NSLog(@"this&is&ios&TestTowParameter=%@&&Second=%@",message1,message2);&&
下面我们在weibview加载完成之后调用测试一下
-(void)webViewDidFinishLoad:(UIWebView&*)webView&&
& & & JSContext&*context=[webView&valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; &
& & & TestJSObject&*testJO=[TestJSObject&new]; &
& & & context[@"testobject"]=testJO; &
& & & NSString&*jsStr1=@"testobject.TestNOParameter()"; &
& & & [context&evaluateScript:jsStr1]; &
& & & NSString&*jsStr2=@"testobject.TestOneParameter('参数1')"; &
& & & [context&evaluateScript:jsStr2]; &
& & & NSString&*jsStr3=@"testobject.TestTowParameterSecondParameter('参数A','参数B')"; &
& & & [context&evaluateScript:jsStr3];&&
TestJSOC[] this is ios TestNOParameter
TestJSOC[] this is ios TestOneParameter=参数1
TestJSOC[] this is ios TestTwoParameter=参数A Second=参数B
好了 我们看到了结果 三个方法都调用了&
ok两种方式都完成了
/javascriptcore/
http://www.webryan.net/2013/10/about-ios7-javascriptcore-framework/
优质网站模板webview调用javascript获得返回值 - ITeye问答
&!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&
&html xmlns="http://www.w3.org/1999/xhtml"&
&meta http-equiv="Content-Type" content="text/ charset=UTF-8" /&
&title&test&/title&
&script type="text/javascript"&
function test_function_add(a,b)
return a+b;
function android_ret(tag,val)
alert(tag+":"+val);
function android_excute(tag,js)
var retval = eval(js);
android_ret(tag,retval);
function getCellString(row, column)
return Report.getCellString(row,column);
function getCookie(name)//取cookies函数
var arr = document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)"));
if(arr != null){
test.getCookie(name,unescape(arr[2]));
test.getCookie(name,"null");
安卓中,想要调用html里面的方法,并且获得返回值。应该怎么去获得?
webView.loadUrl("javascript:android_excute('test','test_function_add(1,6)'");
& webView.addJavascriptInterface(new JavaScriptInterface(), "Report");
& webView.loadUrl("javascript:getCellString('Report',getCellString(5,7)");
这样都获得不了。麻烦问下具体应该怎样去获得
问题补充例如 ,我要获得一个int类型的返回值,调用第一个test_function_add方法。应该怎么去获得?
执行js函数的代码
webView.loadUrl("javascript:resetFontSize(20px)"); 这句
不能直接在加载页面的下面。
因为loadUrl是个异步的加载方法,一开始html页面并没有加载完,
document也没生成完,所以resetFontSize方法无法解析document树,
把代码改成这样
webView.loadUrl("file:///android_asset/.html");
WebViewClient wvc = new WebViewClient() {
public void onPageFinished(WebView view, String url) {
webView.loadUrl("javascript:resetFontSize('20px')");
super.onPageFinished(view, url);
webView.setWebViewClient(wvc);
}
我已经测试通过。
已解决问题
未解决问题iOS开发之用javascript调用oc方法而非url
作者:小小流浪的汉子
字体:[ ] 类型:转载 时间:
本文说的是非拦截URL进行判断,然后调用oc方法这种调用手段,所以不用讨论说为什么不用url这种方法,具体看需求,有需要的可以参考。
先来看看如何在项目中的webview上面点击一个按钮,就能达到调用oc代码
上面的这个页面是webview里面嵌套的一个项目的网页,打印订单点击之后(点击事件是一个js方法),需要调用oc里面集成好的蓝牙打印机功能,来完成打印。
所以这里只能用js代码来直接调用oc代码。
1.首先创建一个iOS类,因为这里一般都需要安卓端做一套,iOS端做一套,所以一般这样命名以示区别
#import &Foundation/Foundation.h&
#import &JavaScriptCore/JavaScriptCore.h&
* js调用oc里 main的代码,需要借助这个协议才行
@protocol JSObjectProtocol &JSExport&
#pragma mark -js调用该oc方法,并且将jsonstring打印出来
- (void)print:(NSString *)jsonS
@interface iOS : NSObject&JSObjectProtocol&
@implementation iOS
#pragma mark -打印jsonString
- (void)print:(NSString *)jsonString{
//打印的具体事件
2. 在网页加载结束之后调用注册iOS注册
#pragma mark -网页加载完毕,注册ios对象,并且自动检查打印机是否自动连接
-(void)webViewDidFinishLoad:(UIWebView *)webView{
//首先创建JSContext 对象(此处通过当前webView的键获取到jscontext)
//这个对象必须创建出来,否则会无法调用到协议里的方法
iOS *ios = [[iOS alloc] init];
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"iOS"] =
3. 在js的点击事件里面类似下面这样调用即可(下面这段代码写在对应的js代码里面,而非你的oc代码里面)
if (window["Android"] && window["Android"]["jsPrint"]) {
var andPrint = window.Android.jsPrint(JSON.stringify(d));
} else if (window["iOS"] && window["iOS"]["print"]) {
window["iOS"]["print"](JSON.stringify(d));
this.alert.autoCloseTip("找不到打印方法!", 1000);
以上就是关于iOS如何使用javascript代码调用oc方法的全部内容,希望对大家学习IOS开发有所帮助。希望大家多多支持脚本之家。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具17:45 提问
js和iOS之间的交互的问题
在js端发出一个请求,在iOS的原生代码中,也就是webView去加载链接的时候,先去拦截url,拦截后,在iOS端去请求数据,请求下来数据后,把拿到的数据再去给js,然后js开始发出的链接的请求就会在js端返回iOS的请求数据。注意的是在js端并没有什么方法名之类的,js仅仅只是发出个url的请求。
按赞数排序
----------------------biu~biu~biu~~~在下问答机器人小D,这是我依靠自己的聪明才智给出的答案,如果不正确,你来咬我啊!
这个没办法做到,只能js和iOS互相调用。没办法修改hook
如果只是在webview请求链接之前,拦截url,这个在webview的delegate中是有方法的。- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType。此方法会在跳转加载网页之前执行。
建议很好用Cordova 或者叫phonegap的三方框架,嵌入一个CDVWebViewController,然后开发你自己的插件。当然,这需要后台也使用Cordova的一个JS库。webView本人只提供了一个很low的JS原生交互接口:evluateJavaScript,可以执行web内的js调用,返回值只能是一个对象,就是这个方法的返回值。如果是多个对象,js方面要对数据做包装,只能有一个返回值。
其他相关推荐Objective-c与js交互专题
在写 Script 的时候,可以使用一个叫做 window 的对象,像是我们想要从现在的网页跳到另外一个网页的时候,就会去修改 window.location.href 的位置;在我们的 Objective-C 程序码中,如果我们可以取得指定的 WebView 对象,也就可以拿到这个出现在
中的 window 对象,也就是 [webView windowScriptObject]。
这个对象就是 WebView 里头的 JS 与我们的 Objective-C程序之间的桥梁&&window 对象可以取得网页里头所有的 JS 函数与对象,而如果我们把一个 Objective-C 对象设定成 windowScriptObject 的 value,JS 也便可以调用Objective-C对象的 method。于是,我们可以在Objective-C 程序里头要求 WebView 执行一段 JS,也可以反过来让 JS 调用一段用 Obj C 实现的功能。
由于Objective-C 与 JS 本身的语言特性不同,在两种语言之间相互传递东西之间,就可以看到两者的差别:
JS 虽然是 OO,但是并没有 class,所以将 JS 对象传到 Obj C 程序里头,除了基本字串会转换成 NSString、基本数字会转成 NSNumber,像是 Array 等其他对象,在 Objective-C 中,都是 WebScriptObject 这个 Class。意思就是,JS 的 Array 不会帮你转换成 NSArray。从 JS 里头传一个空对象给 Objective-C 程序,用的不是 Objective-C 里头原本表示「没有东西」的方式,像是 NULL、nil、NSNull 等,而是专属 WebKit 使用的 WebUndefined。
2 具体技巧
2.1 用Objective-C 取得与设定JavaScript 对象
要从 Objective-C取得网页中的 JavaScript 对象,也就是对 windowScriptObject 做一些 KVC 调用,像是 valueForKey: 与 valueForKeyPath:。如果我们在 JS 里头,想要知道目前的网页位置,会这么写:
var location = window.location.
用 Objective-C 就可以这么调用:
NSString *location = [[webView windowScriptObject] valueForKeyPath:@location.href];
如果我们要设定 window.location.href,要求开启另外一个网页,在 JS 里头:
window.location.href = 'https://spring-studio.net';
在Objective-C:
[[webView windowScriptObject] setValue:@https://spring-studio.netforKeyPath:@location.href];
2.2 用Objective C 调用 JavaScript function
2.2.1 用 evaluateWebScript: 执行
要用 Objective-C 调用网页中的 JS function,大概有几种方法。第一种是直接写一段跟你在网页中会撰写的 JS 一模一样的程序,叫 windowScriptObject 用 evaluateWebScript: 执行。
例如,我们想要在网页中产生一个新的 JS function,内容是:
function x(x) {
return x + 1;
所以在 Objective-C 中可以这样写;
[[webView windowScriptObject] evaluateWebScript:@function x(x) { return x + 1;}];
接下来我们就可以调用 window.x():
NSNumber *result = [[webView windowScriptObject] evaluateWebScript:@x(1)];
NSLog(@result:%d, [result integerValue]); // Returns 2
2.2.2 用function对象WebScriptObject执行自己
由于在 JS 中,每个 funciton 其实都是对象,所以我们还可以直接取得 window.x 叫这个对象执行自己。
在 JS 里头如果这样写:
window.x.call(window.x, 1);
Objective-C 中便是这样:
WebScriptObject *x = [[webView windowScriptObject] valueForKey:@x];
NSNumber *result = [x callWebScriptMethod:@call withArguments:[NSArray arrayWithObjects:x, [NSNumbernumberWithInt:1], nil]];
这种让某个 WebScriptObject 自己执行自己的写法,其实比较不会用于从 Objective-C 调用 JS 这一端,而是接下来会提到的,由 JS 调用 Objective-C,因为这样 JS 就可以把一个 callback function 送到 Objective-C 程序里头。
2.2.3 利用DOM对象
WebKit 里头,所有的 DOM 对象都继承自 DOMObject,DOMObject 又继承自 WebScriptObject,所以我们在取得了某个 DOM 对象之后,也可以从 Objective-C 程序中,要求这个 DOM 对象执行 JS 程序。
假如我们的网页中,有一个 id 叫做 &#s& 的文字输入框(text input),而我们希望现在键盘输入的焦点放在这个输入框上,在 JS 里头会这样写:
document.querySelector('#s').focus();
在Objective-C中写法:
DOMDocument *document = [[webView mainFrame] DOMDocument];
[[document querySelector:@#s] callWebScriptMethod: @focuswithArguments:nil];
2.3 用JavaScript存取Objective-C的Value
要让网页中的 JS 程序可以调用 Objective-C 对象,方法是把某个 Objective-C 对象注册成 JS 中 window 对象的属性。之后,JS 便也可以调用这个对象的 method,也可以取得这个对象的各种 Value,只要是 KVC 可以取得的 Value,像是 NSString、NSNumber、NSDate、NSArray、NSDictionary、NSValue&等。JS 传 Array 到 Objective-C 时,还需要做些特别处理才能变成 NSArray,从 Obj C 传一个 NSArray 到 JS 时,会自动变成 JS Array。
2.3.1 将Objective-C对象注册给window对象的时机
首先我们要注意的是将 Objective-C 对象注册给 window 对象的时机,由于每次重新载入网页,window 对象的内容都会有所变动-毕竟每个网页都会有不同的 JS 程序,所以,我们需要在适当的时机做这件事情。我们首先要指定 WebView 的 frame loading delegate(用 setFrameLoadDelegate:),并且实现 webView:didClearWindowObject:forFrame:,WebView 只要更新了 windowScriptObject,就会调用这一段程序。
假如我们现在要让网页中的 JS 可以使用目前的 controller 对象,会这样写:
- (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)windowObject forFrame:(WebFrame *)frame
[windowObject setValue:self forKey:@controller];
如此一来,只要调用 window.controller,就可以调用我们的 Objective-C 对象。
2.3.2 JS中存取 Objective-C 对象的 Value
假如我们的 Objective-C Class 里头有这些成员变量:
@interface MyController : NSObject
IBOutlet WebView *webV
IBOUtlet NSWindow *
NSString *stringV
NSInteger numberV
NSArray *arrayV
NSDate *dateV
NSDictionary *dictV
NSRect frameV
指定一下 Value:
stringValue = @
numberValue = 24;
arrayValue = [[NSArray arrayWithObjects:@text, [NSNumbernumberWithInt:30], nil] retain];
dateValue = [[NSDate date] retain];
dictValue = [[NSDictionary dictionaryWithObjectsAndKeys:@value1,@key1, @value2, @key2, @value3, @key3, nil] retain];
frameValue = [window frame];
用 JS 读读看:
var c = window.
var main = document.getElementById('main');
var HTML = '';
HTML += '
' + c.stringValue + '
HTML += '
' + c.numberValue + '
HTML += '
' + c.arrayValue + '
HTML += '
' + c.dateValue + '
HTML += '
' + c.dictValue + '
HTML += '
' + c.frameValue + '
main.innerHTML = HTML;
结果如下:
string 24 text,30
00:01:04 +0800 { key1 = value1; key2 = value2; key3 = value3; } NSRect: {{275, 72}, {570, 657}}
不过,如果你看完上面的范例,就直接照做,应该不会直接成功出现正确的结果,而是会拿到一堆 undefined,原因是,Objective-C 对象的 Value 预设被保护起来,不会让 JS 直接存取。要让 JS 可以存取 Objective-C 对象的 Value,需要操作 +isKeyExcludedFromWebScript: 针对传入的 Key 一一处理,如果我们希望 JS 可以存取这个 key,就回传 NO:
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name
if (!strcmp(name, stringValue)) {
return NO;
return YES;
除了可以读取 Objective-C对象的 Value 外,也可以设定 Value,相当于在 Objective-C中使用 setValue:forKey:,如果在上面的 JS 程序中,我们想要修改 stringValue,直接调用 c.stringValue = &new value& 即可。像前面提到,在这裡传给 Objective-C的 JS 对象,除了字串与数字外,class 都是 WebScriptObject,空对象是 WebUndefined。
2.4 用 JavaScript调用 Objective-C方法
2.4.1 方法写法差异
Objective-C 的语法沿袭自 SmallTalk,Objective-C 的 selector,与 JS 的 function 语法有相当的差异。WebKit 预设的实事是,如果我们要在 JS 调用 Objective-C selector,就是把所有的参数往后面摆,并且把所有的冒号改成底线,而原来 selector 如果有底线的话,又要另外处理。
假使我们的 controller 对象有个 method,在 Objective-C 中写成这样:
- (void)setA:(id)a b:(id)b c:(id)c;
在 JS 中就这么调用:
controller.setA_b_c_('a', 'b', 'c');
2.4.2 给方法取别名
实在有点丑。所以 WebKit 提供一个方法,可以让我们把某个 Objective-C selector 变成好看一点的 JS function。我们要实现 webScriptNameForSelector:
+ (NSString *)webScriptNameForSelector:(SEL)selector
if (selector == @selector(setA:b:c:)) {
return @setABC;
以后就可以这么调用:
controller.setABC('a', 'b', 'c');
我们同样可以决定哪些 selector 可以给 JS 使用,哪些要保护起来,方法是实作 isSelectorExcludedFromWebScript:。而我们可以改变某个 Objective-C selector 在 JS 中的名称,我们也可以改变某个 value 的 key,方法是实作 webScriptNameForKey:。
2.4.3 注意
有几件事情需要注意一下:
用 JavaScript 调用 Objective C 2.0 的 property。在上面,我们用 JS 调用 window.controller.stringValue,与设定里头的 value 时,这边很像我们使用 Objective-C 2.0 的语法,但其实做的是不一样的事情。用 JS 调用 controller.stringValue,对应到的 Objective-C 语法是 [controller valueForKey:@stringValue],而不是调用 Objective-C 对象的 property。
如果我们的 Objective-C 对象有个 property 叫做 stringValue,我们知道,Objective-C property 其实会在编译时,变成 getter/setter method,在 JS 里头,我们便应该要调用 controller.stringValue() 与 controller.setStringValue_()。
Javascript 中,Function 即对象的特性
JS 的 function 是对象,当一个 Objective-C 对象的 method 出现在 JS 中时,这个 method 在 JS 中,也可以或多或少当做对象处理。我们在上面产生了 setABC,也可以试试看把它倒出来瞧瞧:
console.log(controller.setABC);
我们可以从结果看到:
function setABC() { [native code] }
这个 function 是 native code。因为是 native code,所以我们无法对这个 function 调用 call 或是 apply。
另外,在把我们的 Objective-C 对象注册成 window.controller 后,我们会许也会想要让controller变成一个 function 来执行,像是调用 window.controller();或是,我们就只想要产生一个可以让 JS 调用的 function,而不是整个对象都放进 JS 里头。我们只要在 Objective-C 对象中,实现invokeDefaultMethodWithArguments:,就可以回传在调用 window.controller() 时想要的结果。
2.4.4 示例
现在我们可以综合练习一下。前面提到,由于我们可以把 JS 对象以 WebScriptObject 这个 class 传入 Obj C 程序,Objective-C 程序中也可以要求执行 WebScriptObject 的各项 function。我们假如想把 A 与 B 两个数字丢进 Objective-C 程序里头做个加法,加完之后出现在网页上,于是我们写了一个 Objective-C method:
- (void)numberWithA:(id)a plusB:(id)b callback:(id)callback
NSInteger result = [a integerValue] + [b integerValue];
[callback callWebScriptMethod:@call withArguments:[NSArrayarrayWithObjects:callback, [NSNumber numberWithInteger:result],nil]];
JS 里头就可以这样调用:
window.controller.numberWithA_plusB_callback_(1, 2,function(result) {
var main = document.getElementById('main');
main.innerText =
3 WebViewJavascriptBridge桥接库
WebViewJavascriptBridge
/marcuswestin/WebViewJavascriptBridge
3.1 机制原理
很明显:WebViewJavascriptBridge.js.txt主要用于衔接UIWebView中的web page,而WebViewJavascriptBridge.h/m则主要用于与ObjC的nativecode打交道。他们作为一个整体,其实起到了一个&桥梁&的作用,这三个文件封装了他们具体的交互处理方式,只开放出一些对外的涉及到业务处理的API,因此你在需要UIWebView与Nativecode交互的时候,引入该库,则无需考虑太多的交互上的问题。整个的Bridge对你来说都是透明的,你感觉的时候,就像是web编程的前端和后端一样清晰。
3.1.1 Native端工作机制
Native端中主要工作机制是,将当前WebView的Delegate截留,用当前Bridge实例对象作为委托Target对象,获取到WebViewDelegate回调方法(主要在shouldStartLoadWithRequest回调方法中进行桥接处理)并加以处理后,再往上回调到目标VC中;具体代码如下所示:
- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(id)webViewDelegate handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle{
_messageHandler = messageH
_webView = webV
_webViewDelegate = webViewD
_messageHandlers = [NSMutableDictionary dictionary];
_webView.delegate =
_resourceBundle =
回调上层方法示例:
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewD
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongDelegate webViewDidFinishLoad:webView];
3.1.2 Native端事件解析处理机制
Js端需要传递的业务数据并不通过Url参数传递,而是通过在Native端调用js方法直接获取数据JSON对象,再将其做解析处理。集中在- (void)_flushMessageQueue方法中处理。核心代码如下:
//1、获取业务数据JSON对象字符串
NSString *messageQueueString = [_webView stringByEvaluatingJavaScriptFromString:@WebViewJavascriptBridge._fetchQueue();];
//2、将对象序列化为数组
id messages = [self _deserializeMessageJSON:messageQueueString];
//3、枚举每个Message对象并作解析处理
NSString* responseId = message[@responseId];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks [responseId];
responseCallback(message[@responseData]);
[_responseCallbacks removeObjectForKey:responseId];
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@callbackId];
if (callbackId) {
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
WVJBMessage* msg = @{ @responseId:callbackId, @responseData:responseData };
[self _queueMessage:msg];
responseCallback = ^(id ignoreResponseData) {
// Do nothing
if (message[@handlerName]) {
handler = _messageHandlers[message[@handlerName]];
handler = _messageH
///!!!在此完成注册事件回调,包括注册事件与接收数据事件
handler(message[@data], responseCallback);
3.1.3 js端工作机制
js端通过iFrame来触发一次load动作,但是iFrame本身的Url并不携带数据,而仅仅是用于触发load动作,具体的业务数据缓存在sendMessageQueue数组中。
Native端捕获到load动作后,再调用window.WebViewJavascriptBridge类的_fetchQueue()方法获取业务数据去做解析。
所以,js端作用主要有三个:
缓存业务数据(包括注册事件);提供公用方法供Native端调用,一般用于获取缓存的业务数据;触发load动作以唤醒与Native的交互动作;
主要公用方法:
handleMessageFromObjC方法
用于供Native端发送数据到js端;
fetchQueue方法:
用于供Native端获取业务数据对象;
registerHandler(handlerName, handler)方法
用于注册js事件方法,用于供Native端调用;
callHandler(handlerName, data, responseCallback)方法
用于调用Native端事件方法。
3.2 功能特性
(1)UI端在初始化时支持设置消息的默认处理器(这里的消息指的是从Native端接收到的消息);
(2)从UI端向Native端发送消息,并支持对于Native端响应后的回调处理的定义;
(3)UI端调用Native定义的处理器,并支持Native端响应后的回调处理定义;
(4)UI端注册处理器(供Native端调用),并支持给Native端响应处理逻辑的定义;
【2】Native端
(1)Native端在初始化时支持设置消息的默认处理器(这里的消息指的是从UI端发送过来的消息);
(2)从Native端向UI端发送消息,并支持对于UI端响应后的回调处理逻辑的定义;
(3)Native端调用UI端定义的处理器,并支持UI端给出响应后在Native端的回调处理逻辑的定义;
(4)Native端注册处理器(供UI端调用),并支持给UI端响应处理逻辑的定义UI端以及Native端完全是对等的两端,实现也是对等的。一段是消息的发送端,另一段就是接收端。这里为引起混淆,需要解释一下我这里使用的&响应&、&回调&在这个上下文中的定义:
(1)响应:接收端给予发送端的应答
(2)回调:发送端收到接收端的应答之后在接收端调用的处理逻辑
3.3 使用过程
1.初始化一个webview(viewdidload)
UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:webView];
2.将此webview与WebViewJavascriptBridge关联(viewdidload)
if (_bridge) { }
[WebViewJavascriptBridge enableLogging];
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@ObjC received message from JS: %@, data);
responseCallback(@Response for message from ObjC);
ps:此时你的webview就与js搭上桥了。下面就是方法的互调和参数的互传。
(1) js调oc方法(可以通过data给oc方法传值,使用responseCallback将值再返回给js)
[_bridge registerHandler:@testObjcCallback handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@testObjcCallback called: %@, data);
responseCallback(@Response from testObjcCallback);
这里注意testObjcCallback这个方法的标示。html那边的命名要跟ios这边相同,才能调到这个方法。当然这个名字可以两边商量着自定义。简单明确即可。
(2)oc调js方法(通过data可以传值,通过 response可以接受js那边的返回值 )
id data = @{ @greetingFromObjC: @Hi there, JS! };
[_bridge callHandler:@testJavascriptHandler data:data responseCallback:^(id response) {
NSLog(@testJavascriptHandler responded: %@, response);
注意这里的 testJavascriptHandler也是个方法标示。
(3)oc给js传值(通过 response接受返回值 )
[_bridge send:@A string sent from ObjC to JS responseCallback:^(id response) {
NSLog(@sendMessage got response: %@, response);
(4)oc给js传值(无返回值)
[_bridge send:@A string sent from ObjC after Webview has loaded.];
3.4 JS端使用
3.4.1 初始化示例代码
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
//以事件监听器形式注释初始化方法
document.addEventListener('WebViewJavascriptBridgeReady', function() {
callback(WebViewJavascriptBridge)
connectWebViewJavascriptBridge(function(bridge) {
var uniqueId = 1
function log(message, data) {
var log = document.getElementById('log')
var el = document.createElement('p')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + ':
' + JSON.stringify(data)
if (log.children.length) { log.insertBefore(el, log.children[0]) }
else { log.appendChild(el) }
//调用对象Init初始化方法完成Bridge对象自身的初始化动作
bridge.init(function(message, responseCallback) {
log('JS got a message', message)
var data = { 'Javascript Responds':'Wee!' }
log('JS responding with', data)
responseCallback(data)
//注册一些事件,供Native端调用
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
3.4.2 注册事件供Native调用
//注册一些事件,供Native端调用
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
3.4.3 UI事件发送数据到Native
var button = document.getElementById('buttons').appendChild(document.createElement('button'))
button.innerHTML = 'Send message to ObjC'
button.onclick = function(e) {
e.preventDefault()
var data = 'Hello from JS button'
log('JS sending message', data)
bridge.send(data, function(responseData) {
log('JS got response', responseData)
document.body.appendChild(document.createElement('br'))
3.4.4 UI事件调用Native注册方法
var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
callbackButton.innerHTML = 'Fire testObjcCallback'
callbackButton.onclick = function(e) {
e.preventDefault()
log('JS calling handler testObjcCallback')
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
3.5 Native端使用
3.5.1 初始化
建议在ViewDidLoad中完成
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@ObjC received message from JS: %@, data);
responseCallback(@Response for message from ObjC);
3.5.2 注册事件方法供JS调用
[_bridge registerHandler:@testObjcCallback handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@testObjcCallback called: %@, data);
responseCallback(@Response from testObjcCallback);
3.5.3 发送数据到JS端
[_bridge send:@A string sent from ObjC before Webview has loaded. responseCallback:^(id responseData) {
NSLog(@objc got response! %@, responseData);
3.5.4 调用JS中方法
[_bridge callHandler:@testJavascriptHandler data: @{ @foo:@before ready }];
4 原生+h5页面的APP业务解决方案设想
4.1 js端调用APP方法
WebViewJavascriptBridge支持主动发送数据与调用Native已注册事件方法来与Native端交互。
两种机制各有所长,不过为了保证向后兼容性,建议主要采用主动发送数据的方法来实现与Native端的交互。尽量将UI端的业务抽象、归类,以命令码+子命令码+参数数据的形式,封装到JSON对象中,传递到Native端,Native端的解析逻辑统一采用一个引擎类来集中处理。
这样也利于与两个平台的逻辑统一。
以集成微信支付为例:
因为支付SDK只能用原生端接口,所以可以考虑将各种js端支付操作(发起微信支付、领取卡券等)抽象化成不同类型的命令,用户点击页面&支付&按钮时,h5页面发送支付命令到Native端,Native将命令进行解析后,调起微信SDK中支付接口完成支付,获取到支付结果后再将结果回调给h5页面。
因为这类APP是以信息展示为主的APP,各类商品展示页面肯定会继续使用h5页面,但是涉及到与Native端交互时,就需要一个统一的桥接层来处理各类业务操作,就不要使用各种封装具体某类Native端功能的第三方库了,总的设计思想应该是设计一个统一的桥接层,然后基于此来统一封装Native端的各种能力,这一方面,微信是这么做的、美居也是这么做的。
4.2 APP端调用js方法
APP端调用js方法,建议也采用同样策略,制定一个相互调用规范,明确命令编码,采用json对象传递对象数据。不过App端可提供部分注册方法,供js端来获取APP端通用信息,例如获取设备信息、尺寸屏幕的。

我要回帖

更多关于 js调用url 返回值 的文章

 

随机推荐