H5与iOS交互

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

    //js方法名+参数
    NSString* jsCode = [NSString stringWithFormat:@"report('%@')",self.jsStr];

    //调用html页面的js方法
    [webView stringByEvaluatingJavaScriptFromString:jsCode];

}

资料:
1 原理
在写 JavaScript 的时候,可以使用一个叫做 window 的对象,像是我们想要从现在的网页跳到另外一个网页的时候,就会去修改 window.location.href 的位置;在我们的 Objective-C 程序码中,如果我们可以取得指定的 WebView 对象,也就可以拿到这个出现在 JavaScript 中的 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.href;

用 Objective-C 就可以这么调用:

NSString *location = [[webView windowScriptObject] valueForKeyPath:@location.href];
1
如果我们要设定 window.location.href,要求开启另外一个网页,在 JS 里头:

window.location.href = ‘http://spring-studio.net’;
1
在Objective-C:

[[webView windowScriptObject] setValue:@http://spring-studio.netforKeyPath:@location.href];
1
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;}];
1
接下来我们就可以调用 window.x():

NSNumber *result = [[webView windowScriptObject] evaluateWebScript:@x(1)];
NSLog(@result:%d, [result integerValue]); // Returns 2
1
2
2.2.2 用function对象WebScriptObject执行自己
由于在 JS 中,每个 funciton 其实都是对象,所以我们还可以直接取得 window.x 叫这个对象执行自己。

在 JS 里头如果这样写:

window.x.call(window.x, 1);
1
Objective-C 中便是这样:

WebScriptObject *x = [[webView windowScriptObject] valueForKey:@x];
NSNumber *result = [x callWebScriptMethod:@call withArguments:[NSArray arrayWithObjects:x, [NSNumbernumberWithInt:1], nil]];
1
2
这种让某个 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];
1
2
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];

}
1
2
3
4
5
6
7
如此一来,只要调用 window.controller,就可以调用我们的 Objective-C 对象。

2.3.2 JS中存取 Objective-C 对象的 Value
假如我们的 Objective-C Class 里头有这些成员变量:

@interface MyController : NSObject

{

IBOutlet WebView *webView;

IBOUtlet NSWindow *window;

NSString *stringValue;

NSInteger numberValue;

NSArray *arrayValue;

NSDate *dateValue;

NSDictionary *dictValue;

NSRect frameValue;

}

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
指定一下 Value:

stringValue = @string;

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];
1
2
3
4
5
6
7
8
9
10
11
用 JS 读读看:

var c = window.controller;

var main = document.getElementById(‘main’);

var HTML = ‘’;

if © {

HTML += ’

’ + c.stringValue + ’

';

HTML += ’

’ + c.numberValue + ’

';

HTML += ’

’ + c.arrayValue + ’

';

HTML += ’

’ + c.dateValue + ’

';

HTML += ’

’ + c.dictValue + ’

';

HTML += ’

’ + c.frameValue + ’

';

main.innerHTML = HTML;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
结果如下:

string 24 text,30 2010-09-09 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;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
除了可以读取 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?️c:)) {

return @setABC;

}

return nil;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
以后就可以这么调用:

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]];

}
1
2
3
4
5
6
7
8
9
JS 里头就可以这样调用:

window.controller.numberWithA_plusB_callback_(1, 2,function(result) {

var main = document.getElementById(‘main’);

main.innerText = result;

});
1
2
3
4
5
6
7
3 WebViewJavascriptBridge桥接库

WebViewJavascriptBridge

https://github.com/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 = messageHandler;

_webView = webView;

_webViewDelegate = webViewDelegate;

_messageHandlers = [NSMutableDictionary dictionary];

_webView.delegate = self;

_resourceBundle = bundle;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
回调上层方法示例:

__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;

if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {

[strongDelegate webViewDidFinishLoad:webView];

}
1
2
3
4
5
6
7
3.1.2 Native端事件解析处理机制
Js端需要传递的业务数据并不通过Url参数传递,而是通过在Native端调用js方法直接获取数据JSON对象,再将其做解析处理。集中在- (void)_flushMessageQueue方法中处理。核心代码如下:

//1、获取业务数据JSON对象字符串

NSString *messageQueueString = [_webView stringByEvaluatingJavaScriptFromString:@WebViewJavascriptBridge._fetchQueue();];
1
//2、将对象序列化为数组

id messages = [self _deserializeMessageJSON:messageQueueString];
1
//3、枚举每个Message对象并作解析处理

NSString* responseId = message[@responseId];

if (responseId) {

WVJBResponseCallback responseCallback = _responseCallbacks [responseId];

responseCallback(message[@responseData]);

[_responseCallbacks removeObjectForKey:responseId];

} else {

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];

};

} else {

responseCallback = ^(id ignoreResponseData) {

// Do nothing

};

}

WVJBHandler handler;

if (message[@handlerName]) {

handler = _messageHandlers[message[@handlerName]];

} else {

handler = _messageHandler;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
///!!!在此完成注册事件回调,包括注册事件与接收数据事件

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端

(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];
1
2
3
2.将此webview与WebViewJavascriptBridge关联(viewdidload)

if (_bridge) { return; }

[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);

}];
1
2
3
4
5
6
7
8
9
10
11
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);

}];
1
2
3
4
5
6
7
这里注意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);

}];
1
2
3
4
5
6
7
注意这里的 testJavascriptHandler也是个方法标示。

(3)oc给js传值(通过 response接受返回值 )

[_bridge send:@A string sent from ObjC to JS responseCallback:^(id response) {

NSLog(@sendMessage got response: %@, response);

}];
1
2
3
4
5
(4)oc给js传值(无返回值)

[_bridge send:@A string sent from ObjC after Webview has loaded.];
1
3.4 JS端使用

3.4.1 初始化示例代码

function connectWebViewJavascriptBridge(callback) {

if (window.WebViewJavascriptBridge) {

callback(WebViewJavascriptBridge)

} else {

//以事件监听器形式注释初始化方法

document.addEventListener(‘WebViewJavascriptBridgeReady’, function() {

callback(WebViewJavascriptBridge)

}, false)

}

}

connectWebViewJavascriptBridge(function(bridge) {

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) }

}

//调用对象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)

})

})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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)

})
1
2
3
4
5
6
7
8
9
10
11
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’))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)

})

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);

}];
1
2
3
4
5
6
7
3.5.2 注册事件方法供JS调用

[_bridge registerHandler:@testObjcCallback handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@testObjcCallback called: %@, data);

responseCallback(@Response from testObjcCallback);

}];
1
2
3
4
5
6
7
3.5.3 发送数据到JS端

[_bridge send:@A string sent from ObjC before Webview has loaded. responseCallback:^(id responseData) {

NSLog(@objc got response! %@, responseData);

}];
1
2
3
4
5
3.5.4 调用JS中方法

[_bridge callHandler:@testJavascriptHandler data: @{ @foo:@before ready }];
1
4 原生+h5页面的APP业务解决方案设想

4.1 js端调用APP方法

WebViewJavascriptBridge支持主动发送数据与调用Native已注册事件方法来与Native端交互。

两种机制各有所长,不过为了保证向后兼容性,建议主要采用主动发送数据的方法来实现与Native端的交互。尽量将UI端的业务抽象、归类,以命令码+子命令码+参数数据的形式,封装到JSON对象中,传递到Native端,Native端的解析逻辑统一采用一个引擎类来集中处理。

这样也利于IOS与Android两个平台的逻辑统一。

以集成微信支付为例:

因为支付SDK只能用原生端接口,所以可以考虑将各种js端支付操作(发起微信支付、领取卡券等)抽象化成不同类型的命令,用户点击页面“支付”按钮时,h5页面发送支付命令到Native端,Native将命令进行解析后,调起微信SDK中支付接口完成支付,获取到支付结果后再将结果回调给h5页面。

因为这类APP是以信息展示为主的APP,各类商品展示页面肯定会继续使用h5页面,但是涉及到与Native端交互时,就需要一个统一的桥接层来处理各类业务操作,就不要使用各种封装具体某类Native端功能的第三方库了,总的设计思想应该是设计一个统一的桥接层,然后基于此来统一封装Native端的各种能力,这一方面,微信是这么做的、美居也是这么做的。

4.2 APP端调用js方法

APP端调用js方法,建议也采用同样策略,制定一个相互调用规范,明确命令编码,采用json对象传递对象数据。不过App端可提供部分注册方法,供js端来获取APP端通用信息,例如获取设备信息、尺寸屏幕的。

二、随着微信的不断推广,在移动开发的过程中原生与H5的混合开发在移动开发中占据着越来越重要的地位;下面我给大家分享一下我在混合开发中的心得!

将HTML页面加载到UIWebview控件中:

A:本地加载:

NSString *webPath = [[NSBundlemainBundle]pathForResource:@“HelloWord"ofType:@"html”];//获取文件路径
NSURL *webURL = [NSURLfileURLWithPath:webPath];//通过文件路径字符串设置URL
NSURLRequest *URLRequest = [NSURLRequestrequestWithURL:webURL];//设置请求提交的相关URL
[self.webView loadRequest:URLRequest];//提交请求
1
2
3
4
B:从服务器加载HTML页面

NSString *urlStr = [NSString stringWithFormat:@"%@/appservice/getAnnouncementDetail.do?id=%@",O2O_URL,self.idStr];
NSURL *url = [NSURL URLWithString:urlStr];
NSLog(@“url:%@”,url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
1
2
3
4
5
2.加入UIWebViewDelegate委托(如果想要进行相应的数据交互必须添加相应的我委托)

-(BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest)request navigationType:(UIWebViewNavigationType)navigationType//html页面的js动作触发时运行

-(void)webViewDidFinishLoad:(UIWebView *)webView //html加载完后触发
1
2
3
3.OC提交JS请求.运行JS方法

[self.webViewstringByEvaluatingJavaScriptFromString:@”方法名(%@)",参数]];
1
4.JS提交请求到OC

-(BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType{
NSString *requestString = [[requestURL]absoluteString];//获取请求的绝对路径.
NSArray *components = [requestStringcomponentsSeparatedByString:@":”];//提交请求时候分割参数的分隔符
if ([componentscount] >1 && [(NSString *)[componentsobjectAtIndex:0]isEqualToString:@“testapp”]) {
//过滤请求是否是我们需要的.不需要的请求不进入条件
if([(NSString *)[components objectAtIndex:1]isEqualToString:@“change”])
{
//所要执行的操作
}
returnNO;
}
returnYES;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
5.在HTML5中JS的写法

1.接收请求的方法写法

function
fund(){
//操作
}
1
2
3
4
2.提交请求的方法写法

function sendCommand(cmd,param){
var url=“testapp:”+cmd+":"+param;//组合请求字符串
document.location = url;//提交的请求
}

你可能感兴趣的:(H5与iOS交互)