JavaScript与原生通信
- UIWebView
- WKWebView
- JavaScripCore
- 第三方库
JS和OC的交互分为两种方式:JS调用OC和OC调用JS,一般的原则是如果JS想传值给OC就用JS调用OC,相反则用OC调用JS
UIWebView
1.JS调用OC
通过UIWebView的代理方法拦截URL
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
当UIWebView在加载之前或者进行网页重定向的会调用这个代理方法,JS调用OC的就这这个代理方法里return YES
UIWebView会继续跳转加载,return NO
可拦截UIWebView的跳转,例如用UIWebView唤起支付宝客户端 :
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSString* reqUrl = request.URL.absoluteString;
if ([reqUrl hasPrefix:@"alipays://"] || [reqUrl hasPrefix:@"alipay://"]) {
[[UIApplication sharedApplication]openURL:request.URL];
return NO;
}
return YES;
}
alipays://
为支付宝唤起的协议,当加载支付宝支付的URL时,只需要拦截request.URL
是否包含此协议,若包含我们只需通过[[UIApplication sharedApplication]openURL:request.URL]
打开这个URL,若安装了支付宝就会唤起支付宝。
我们再来写一个本地h5界面,来看看具体交互。
JS代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h>UIWebView JS 交互</h>
<div style="margin-top: 16px">
<input style="width: 120px" type="button" value="登录" onclick="tap('login')">
<input style="width: 120px" type="button" value="注册" onclick="tap('regist')">
</div>
<script>
function tap(action) {
window.location.href = "obj://" + action;
}
</script>
</body>
</html>
OC代码:
初始化UIWebView,加载本地HTML
UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]];
webView.delegate = self;
[self.view addSubview:webView];
代理方法:
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *url = request.URL.absoluteString;
if ([url containsString:@"obj://regist"]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"开始注册" message:@"注册" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}]];
[self presentViewController:alert animated:YES completion:nil];
return NO;
}
return YES;
}
如图运行效果,当我们点击注册
的时候,就会重定向我们自定义的URLobj://regist
,UIWebView的代理方法就会调用,然后就如上述支付宝一样,拦截后return NO
,弹出警告框,当然也可以传递参数给Native。
2.OC调用JS
OC最简单就是通过URL传递参数,跟GET
请求一样拼接在后面,
OC还可以通过以下方法直接调取JS并传递参数方法如下
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
还用上述的本地的HTML来演示,我需要在JS中添加一个方法nativeCall
JS部分如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h>UIWebView JS 交互</h>
<div style="margin-top: 16px">
<input style="width: 120px" type="button" value="登录" onclick="tap('login')">
<input style="width: 120px" type="button" value="注册" onclick="tap('regist')">
</div>
<script>
function tap(action) {
window.location.href = "obj://" + action;
}
function nativeCall(message) {
alert(message);
}
</script>
</body>
</html>
OC代理方法
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *url = request.URL.absoluteString;
if ([url containsString:@"obj://login"]) {
NSString *message = [NSString stringWithFormat:@"nativeCall('%@')", @"hello javascript"];
[theWebView stringByEvaluatingJavaScriptFromString:message];
return NO;
}
if ([url containsString:@"obj://regist"]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"开始注册" message:@"注册" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}]];
[self presentViewController:alert animated:YES completion:nil];
return NO;
}
return YES;
}
当点击登录
时,我们拦截了登录,然后用该方法直接调取了JS中的方法nativeCall()
, 并传递了参数 hello javascript
,此时必须注意,传递参数的时候需要用单引号,参数多的,用,
隔开即可。
我们也可以将一个字典对象通过反序列化成一个NSString
来传递给JS,例如:
JS代码
<input style="width: 120px" type="button" value="我是谁" onclick="tap('introduce')">
function introduce (person) {
var people = JSON.parse(person);
alert('我是:' + people.name + ',爱好:' + people.like);
}
OC代码
if ([url containsString:@"obj://introduce"]) {
NSDictionary *dic = @{@"name" : @"leevi", @"like" : @"woman"};
NSData *dicData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil];
NSString *str = [[NSString alloc] initWithData:dicData encoding:NSUTF8StringEncoding];
NSString *message = [NSString stringWithFormat:@"introduce('%@')", str];
[theWebView stringByEvaluatingJavaScriptFromString:message];
return NO;
}
运行效果如下:
但这个方法只能传字符串给JS,没法传一个OC的对象,之后我们通过JavaScriptCore
可以实现传一个OC对象。
该方法是有返回值的,我们可以直接在JS的方法中返回给Native参数。
JS
function introduce (person) {
var people = JSON.parse(person);
alert('我是:' + people.name + ',爱好:' + people.like);
return '啦啦';
}
OC
NSString *jsReturn = [theWebView stringByEvaluatingJavaScriptFromString:message];
NSLog(@"%@", jsReturn);
打印