Objective-C与JavaScript交互的两种方式

1、在iOS项目开发中,原生页面中掺杂Html5页面已不是什么新鲜事了,毕竟H5也他相关的优势,比如布局、富文本内容展示等;

2、随着H5页面的侵入,OC与JS的交互意愿越来越强烈,就目前来看,在不引用第三方库的前提下,有两种方式可以解决;
2.1、iOS6及以前通过拦截NSRequest请求来调用原生方法进行交互;
2.2、iOS7及以后的版本苹果官方引入了JavaScriptCore框架;

3、拦截NSRequest请求
A、UIwebView有个代理方法:

1
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

B、此方法通过返回YES/NO来决定是否跳转到request的页面,因此我们可以通过协议头来区分是正常的URL请求还是本地方法的调用请求;
C、比如说网页中某a标签请求的地址是 saap://share,那么在该代理方法中就可以通过url的协议头来将其拦截,返回NO,页面不跳转,同时执行某个share方法,即达到了调用OC本地方法的效果;
D、参数可以通过url中带过来,或者参数内容过长可以通过post请求,在request的HTTPBody中可以将post请求内容解析出来
E、现在调OC方法没问题了,OC如何调用页面中的方法呢?其实UIWebView给我们也提供了一个交互方法

1
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

直接传入JS脚本,还可以接收脚本的返回值

4、JavaScriptCore框架
4.1、在iOS7及以后的版本中可以通过官方提供的JS框架来进行交互,主要涉及到的两个类是JSValue、JSContext
4.2、JSContext指的是JS的上下文,即可以简单解理成标签部分;
4.3、JSValue是一个在OC和JS之间进行数据转换的类,他可以将JS对象或数据转换成OC的对象或数据,反之亦然;
4.4、如何使用呢,又分为两种方式,一种方式通过Block(即所谓的匿名函数),另一种方法即通过代理,这是苹果的一惯作风!

5、两种方法的对比
5.1、Block的基本实现方式:
A、在UIWebView的代理方法- (void)webViewDidFinishLoad:(UIWebView *)webView加入以下代码

1
2
3
4
5
6
7
8
9
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
????JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
????context[@"ocShow"] = ^(){
????????NSLog(@"来自JS的问候...");
????};
????// 模拟JS调用
????[context evaluateScript:@"ocShow()"];
}

网页中如果JS调用ocShow()方法,即会输入这行”来自JS的问候…”;

1
2016-03-17 17:04:34.937 SAJavaScript[8339:233216] 来自JS的问候...

B、需要传参数怎么办?两种方法
C、一种方法是在这个Block中添加参数:^(NSString *str1, NSString *str2){},如果对Block不熟悉可以参考http://www.yusian.com/thread-976-1-1.html 或者网上找下相关资料看看先;
D、另外一种方法,通过JSContext的类方法currentArguments可以获取NSArray *argument = [JSContext currentArguments]; 然后遍历获得;
E、这种方法使用简单方便,但每次都要重新写,可不可以指向我已有的现成方法?当然是可以的!使用代理
5.2、通过代理实现
A、自定义一个协议,继承自,把JS需要调用的方法在该协议中申明;
B、但凡遵循协议的对象都可以被JS所调用
C、实现相关方法即可

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
//
//? ViewController.m
//? SAJavaScript
//
//? Created by 余西安 on 16/3/17.
//? Copyright ? 2016年 Sian. All rights reserved.
//
?
#import "ViewController.h"
#import <JavaScriptCore/JavaScriptCore.h>
?
@protocol SAJSExport <JSExport>
- (void)showMessage:(JSValue *)message;
@end
?
@interface ViewController () <UIWebViewDelegate, SAJSExport>
@property (nonatomic, strong) UIWebView *webView;
@end
?
@implementation ViewController
?
- (void)viewDidLoad
{
????[super viewDidLoad];
????self.webView = [[UIWebView alloc] init];
????self.webView.delegate = self;
????self.webView.frame = self.view.bounds;
????[self.view addSubview:self.webView];
????NSURL *url = [NSURL URLWithString:@"http://www.yusian.com/blog"];
????[self.webView loadRequest:[NSURLRequest requestWithURL:url]];
}
?
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
????JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
????// 1、Block方法
????context[@"ocShow"] = ^(){
????????NSLog(@"来自JS的问候...");
????};
????// 模拟JS调用
????[context evaluateScript:@"ocShow()"];
?????
????// 2、代理方法
????[context setObject:self forKeyedSubscript:@"webView"];
????// 模拟JS调用webView.showMessage()方法
????[context evaluateScript:@"webView.showMessage('Hello JavaScript')"];
}
?
- (void)showMessage:(JSValue *)message
{
????[[[UIAlertView alloc] initWithTitle:nil message:[message toString] delegate:nil cancelButtonTitle:@"朕知道了" otherButtonTitles:nil, nil] show];
}
@end

D、效果图

E、回调JS就更简单了,其实上面已经调用过了,JSContext中的方法

1
- (JSValue *)evaluateScript:(NSString *)script;

F、JSValue与OC数据转换对应关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@textblock
???Objective-C type? |?? JavaScript type
?--------------------+---------------------
?????????nil???????? |???? undefined
????????NSNull?????? |??????? null
???????NSString????? |?????? string
???????NSNumber????? |?? number, boolean
?????NSDictionary??? |?? Object object
???????NSArray?????? |??? Array object
????????NSDate?????? |???? Date object
???????NSBlock (1)?? |?? Function object (1)
??????????id (2)???? |?? Wrapper object (2)
????????Class (3)??? | Constructor object (3)
@/textblock

6、Demo下载

SAJavaScript

3 thoughts on “Objective-C与JavaScript交互的两种方式

  1. Pingback: OC与JavaScript交互刷新页面失效的问题 | 小龙虾博客 (Crayfish)

  2. Pingback: OC中JavaScriptCore使用的两个问题 | 小龙虾博客 (Crayfish)

Leave a Reply