关于UIWebView与js交互的问题

这个问题,在网上能看到很多,主要经过调用UIWebView的一个方法stringByEvaluatingJavaScriptFromStringhtml

来实现,与js的交互问题,我想可能用的过程当中会出现很多问题,因此,先开个文章来存放,等过2天用的时候写个问题,总结下遇到的问题或者经验什么的。linux

http://www.cnblogs.com/zhwl/archive/2013/04/19/3030244.htmlweb

这个网址里的内容不错,很多使用上面所说的那个方法,并且不仅是单单的用oc调用js,还有用js调用oc!spring

在写 JavaScript 的时候,可使用一个叫作 window 的对象,像是咱们想要从如今的网页跳到另一个网页的时候,就会去修改 window.location.href 的位置;在咱们的 Oective C 程序码中,若是咱们能够取得指定的 WebView 的指标,也就能够拿到这个出如今 JavaScript 中的 window 对象,也就是 [webView windowScriptOect]。函数

这个对象就是 WebView 里头的 JS 与咱们的 O C 程序之间的桥樑-window 对象能够取得网页里头全部的 JS 函数与对象,而若是咱们把一个 O C 对象设定成 windowScriptOect 的 value,JS 也即可以调用 O C 对象的 method。因而,咱们能够在 O C 程序里头要求 WebView 执行一段 JS,也能够反过来让 JS 调用一段用 O C 实做的功能。lua

※ 用 Oective C 取得与设定 JavaScript 对象spa

要从 O C 取得网页中的 JavaScript 对象,也就是对 windowScriptOect 作一些 KVC 调用,像是 valueForKey: 与 valueForKeyPath:。若是咱们在 JS 里头,想要知道目前的网页位置,会这麽写:.net

varlocation = window.location.href;

用 OC 就能够这麽调用:调试

NSString*location = [[webView windowScriptOect] valueForKeyPath:@"location.href"];
code

若是咱们要设定 window.location.href,要求开启另一个网页,在 JS 里头:

window.location.href ='http://spring-studio.net';

O C:

[[webView windowScriptOect] setValue:@"http://spring-studio.net"forKeyPath:@"location.href"];

因为 O C 与 JS 自己的语言特性不一样,在两种语言之间相互传递东西之间,就能够看到二者的差异-

 

  • JS 虽然是 OO,可是并无 class,因此将 JS 对象传到 O C 程序里头,除了基本字串会转换成 NSString、基本数字会转成 NSNumber,像是 Array 等其余对象,在 O C 中,都是 WebScriptOect 这个 Class。意思就是,JS 的 Array 不会帮你转换成 NSArray。
  • 从 JS 里头传一个空对象给 O C 程序,用的不是 O C 里头本来表示「没有东西」的方式,像是 NULL、nil、NSNull 等,而是专属 WebKit 使用的 WebUndefined。 

因此,若是咱们想要看一个 JS Array 里头有什麽东西,就要先取得这个对象里头叫作 length 的 value,而后用 webScriptValueAtIndex: 去看在该 index 位置的内容。假如咱们在 JS 里头这样写:

varJSArray = {'zonble','dot','net'};
for(vari = 0; i < JSArray.length; i++) {
    console.log(JSArray[i]);
}

O C 里头就会变成这样:

WebScriptOect *O = (WebScriptOect *)JSArray;
NSUIntegercount = [[O valueForKey:@"length"] integerValue];
NSMutableArray*a = [NSMutableArrayarray];
for(NSUIntegeri = 0; i < count; i++) {
    NSString*item = [O webScriptValueAtIndex:i];
    NSLog(@"item:%@", item);
}

 ※ 用 Oective C 调用 JavaScript function

 

要用 O C 调用网页中的 JS function,大概有几种方法。第一种是直接写一段跟你在网页中会撰写的 JS 如出一辙的程序,叫 windowScriptOect 用 evaluateWebScript: 执行。例如,咱们想要在网页中产生一个新的 JS function,内容是:

 

functionx(x) {
    returnx + 1;
}

因此在 O C 中能够这样写;

[[webView windowScriptOect] evaluateWebScript:@"function x(x) { return x + 1;}"];

接下来咱们就能够调用 window.x(): 

NSNumber*result = [[webView windowScriptOect] evaluateWebScript:@"x(1)"];
NSLog(@"result:%d", [result integerValue]);// Returns 2

因为在 JS 中,每一个 funciton 其实都是对象,因此咱们还能够直接取得 window.x 叫这个对象执行本身。在 JS 里头若是这样写:

window.x.call(window.x, 1);

O C 中即是这样:

WebScriptOect *x = [[webView windowScriptOect] valueForKey:@"x"];
NSNumber*result = [x callWebScriptMethod:@"call"withArguments:[NSArrayarrayWithOects:x, [NSNumbernumberWithInt:1],nil]];

这种让某个 WebScriptOect 本身执行本身的写法,其实比较不会用于从 O C 调用 JS 这一端,而是接下来会提到的,由 JS 调用 O C,由于这样 JS 就能够把一个 callback function 送到 O C 程序里头。

若是咱们在作网页,咱们只想要更新网页中的一个区块,就会利用 AJAX 的技巧,只对这个区块须要的资料,对 server 发出 request,而且在 request 完成的时候,要求执行一段 callback function,更新这一个区块的显示内容。从 JS 调用 O C也能够作相似的事情,若是 O C 程序里头须要必定时间的运算,或是咱们多是在 O C 里头抓取网路资料,咱们即可以把一个 callback function 送到 O C 程序裡,要求 O C 程序在作完工做后,执行这段 callback function。

※ DOM

WebKit 里头,全部的 DOM 对象都继承自 DOMObject,DOMObject 又继承自 WebScriptObject,因此咱们在取得了某个 DOM 对象以后,也能够从 Obj C 程序中,要求这个 DOM 对象执行 JS 程序。

假如咱们的网页中,有一个 id 叫作 “#s” 的文字输入框(text input),而咱们但愿如今键盘输入的焦点放在这个输入框上,在 JS 里头会这样写:

document.querySelector('#s').focus();

Obj C:

DOMDocument *document = [[webView mainFrame] DOMDocument];
[[document querySelector:@"#s"] callWebScriptMethod:@"focus"withArguments:nil];

 

※ 用 JavaScript 存取 Objective C 的 Value

 

要让网页中的 JS 程序能够调用 Obj C 对象,方法是把某个 Obj C 对象注册成 JS 中 window 对象的属性。以后,JS 便也能够调用这个对象的 method,也能够取得这个对象的各类 Value,只要是 KVC 能够取得的 Value,像是 NSString、NSNumber、NSDate、NSArray、NSDictionary、NSValue…等。JS 传 Array 到 ObjC 时,还须要特别作些处理才能变成 NSArray,从 Obj C 传一个 NSArray 到 JS 时,会自动变成 JS Array。

 

首先咱们要注意的是将 Obj 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:selfforKey:@"controller"];
}

 

如此一来,只要调用 window.controller,就能够调用咱们的 Obj C 对象。假如咱们的 Obj C Class 里头有这些成员变数:

 

@interfaceMyController :NSObject
{
    IBOutletWebView *webView;
    IBOUtlet NSWindow*window;
    NSString*stringValue;
    NSIntegernumberValue;
    NSArray*arrayValue;
    NSDate*dateValue;
    NSDictionary*dictValue;
    NSRectframeValue;
}
@end

 

指定一下 Value:

 

stringValue =@"string";
numberValue = 24;
arrayValue = [[NSArrayarrayWithObjects:@"text", [NSNumbernumberWithInt:30],nil] retain];
dateValue = [[NSDatedate] retain];
dictValue = [[NSDictionarydictionaryWithObjectsAndKeys:@"value1",@"key1",@"value2",@"key2",@"value3",@"key3",nil] retain];
frameValue = [window frame];

 

用 JS 读读看:

 

varc = window.controller;
varmain = document.getElementById('main');
varHTML ='';
if(c) {
    HTML +='<p>'+ c.stringValue +'<p>';
    HTML +='<p>'+ c.numberValue +'<p>';
    HTML +='<p>'+ c.arrayValue +'<p>';
    HTML +='<p>'+ c.dateValue +'<p>';
    HTML +='<p>'+ c.dictValue +'<p>';
    HTML +='<p>'+ c.frameValue +'<p>';
    main.innerHTML = HTML;
}

 

结果以下:

 

string24text,302010-09-09 00:01:04 +0800{ key1 = value1; key2 = value2; key3 = value3; }NSRect: {{275, 72}, {570, 657}}

 

不过,若是你看完上面的范例,就直接照作,应该不会直接成功出现正确的结果,而是会拿到一堆 undefined,缘由是,Obj C 对象的 Value 预设被保护起来,不会让 JS 直接存取。要让 JS 能够存取 Obj C 对象的 Value,须要实做 +isKeyExcludedFromWebScript: 针对传入的 Key 一一处理,若是咱们但愿 JS 能够存取这个 key,就回传 NO:

 

+ (BOOL)isKeyExcludedFromWebScript:(constchar*)name
{
    if(!strcmp(name,"stringValue")) {
        returnNO;
    }
    returnYES;
}

 

除了能够读取 Obj C 对象的 Value 外,也能够设定 Value,至关于在 Obj C 中使用 setValue:forKey:,若是在上面的 JS 程序中,咱们想要修改 stringValue,直接调用 c.stringValue = ‘new value’ 便可。像前面提到,在这裡传给 Obj C 的 JS 对象,除了字串与数字外,class 都是 WebScriptObject,空对象是 WebUndefined。

 

※ 用 JavaScript 调用 Objective C method

 

Obj C 的语法沿袭自 SmallTalk,Obj C 的 selector,与 JS 的 function 语法有至关的差别。WebKit 预设的实做是,若是咱们要在 JS 调用 Obj C selector,就是把全部的参数日后面摆,而且把全部的冒号改为底线,而原来 selector 若是有底线的话,又要另外处理。假使咱们的 controller 对象有个 method,在 Obj C 中写成这样:

 

- (void)setA:(id)a b:(id)b c:(id)c;

 

在 JS 中就这麽调用:

controller.setA_b_c_('a','b','c');

实在有点丑。因此 WebKit 提供一个方法,可让咱们把某个 Obj C selector 变成好看一点的 JS function。咱们要实做 webScriptNameForSelector:

 

+ (NSString*)webScriptNameForSelector:(SEL)selector
{
    if(selector ==@selector(setA:b:c:)) {
        return@"setABC";
    }
    returnnil;
}

 

之后就能够这麽调用:

 

controller.setABC('a','b','c');

 

咱们一样能够决定哪些 selector 能够给 JS 使用,哪些要保护起来,方法是实做 isSelectorExcludedFromWebScript:。而咱们能够改变某个 Obj C selector 在 JS 中的名称,咱们也能够改变某个 value 的 key,方法是实做 webScriptNameForKey:。

 

有几件事情须要注意一下:

 

用 JavaScript 调用 Objective C 2.0 的 property

 

在上面,咱们用 JS 调用 window.controller.stringValue,与设定里头的 value 时,这边很像咱们使用 Obj C 2.0 的语法,但其实作的是不同的事情。用 JS 调用 controller.stringValue,对应到的 Obj C 语法是 [controller valueForKey:@"stringValue"],而不是调用 Obj C 对象的 property。

 

若是咱们的 Obj C 对象有个 property 叫作 stringValue,咱们知道,Obj C property 其实会在编译时,变成 getter/setter method,在 JS 里头,咱们便应该要调用 controller.stringValue() 与 controller.setStringValue_()。

 

 

 

 

后面的一些就不转贴了,有兴趣的去链接里面查看。没有机器暂时没法知道调试结果。

 

—————————————————————————————————————————————————————————————————————————————————

UIWebView 接管js调用的alert

 http://blog.sina.com.cn/s/blog_6796844601010nxj.html

js oc 互调

http://hi.baidu.com/sadusaga/item/9fabbabb28e826e94fc7fded\

Objective-C与js相互调用及传参数注意

http://www.linuxidc.com/Linux/2013-06/85736.htm