简单介绍
关于JS调用OC代码的实现还是有多种方法的,比如拦截URL、使用
JavaScriptCore/JavaScriptCore.h这个库(iOS7之后开放的)、当然了,github上也有关于JS和OC交互的框架,比如:WebViewJavascriptBridge,star数也是很多的,我这里主要说说前面两种,对于WebViewJavascriptBridge有兴趣的可以去github上下载来了解了解。
第一种:利用拦截URL来实现
网页片段
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p></p>
<div>
<button onclick="showToast();">弹框提示</button>
</div>
<p></p>
<script>
function showToast() {
// 调用OC中的showToast方法
// 1.无参数
window.location.href = 'test://showToast';
// 2.有参数
window.location.href = 'test://showToast_?parama=666666';
}
</script>
</body>
</html>
一.无参数实现
1.主要代码
- (void)viewDidLoad {
[super viewDidLoad];
// 1.webView
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = self.view.bounds;
webView.delegate = self;
[self.view addSubview:webView];
// 2.加载网页
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
}
#pragma mark - UIWebViewDelegate
/**
* webView发送请求之前,都要调用这个方法(拦截请求来做相应的事情)
*/
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = request.URL.absoluteString;
// 协议头是随便写的,为了区分http协议还是调用OC代码。。
NSString *scheme = @"test://";
if ([url containsString:scheme]) {
// JS要调用OC的方法,而不是直接加载请求
NSRange range = [url rangeOfString:scheme];
NSString *methodName = [url substringFromIndex:range.length];
SEL method = NSSelectorFromString(methodName);
[self performSelector:method withObject:nil];
}
- (void)showToast {
// ios 8之后出现的
UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"成功" message:@"JS调用OC代码成功!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleCancel handler:nil];
[alertVc addAction:cancelAction];
[self presentViewController:alertVc animated:YES completion:nil];
// // ios 9之后就废除了
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
// [alert show];
}
2.测试结果
二.有参数实现:跟无参数一样,只是我们自己去截串把参数取出来
1.主要代码
/**
* webView发送请求之前,都要调用这个方法(拦截请求来做相应的事情)
*/
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = request.URL.absoluteString;
// 协议头是随便写的,为了区分http协议还是调用OC代码。。
NSString *scheme = @"test://";
if ([url hasPrefix:scheme]) {
// 1.获取后面的路径
NSString *path = [url substringFromIndex:scheme.length];// showToast:
NSString *path1 = [path stringByReplacingOccurrencesOfString:@"_" withString:@":"];
NSRange range = [path1 rangeOfString:@"?"];
NSString *paramPath = [path1 substringFromIndex:range.location + 1];
NSRange range1 = [paramPath rangeOfString:@"="];
NSString *parama = [paramPath substringFromIndex:range1.location + 1];
// 2.获取方法名
NSString *methodName = [path1 substringToIndex:range.location];
// 转成SEL
SEL sel = NSSelectorFromString(methodName);
[self performSelector:sel withObject:parama];
}
return YES;
}
- (void)showToast:(NSString *)parama {
UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"成功" message:[NSString stringWithFormat:@"JS调用OC代码成功 - JS参数:%@",parama] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleCancel handler:nil];
[alertVc addAction:cancelAction];
[self presentViewController:alertVc animated:YES completion:nil];
}
2.测试结果
第二种:利用JavaScriptCore/JavaScriptCore.h这个库来实现
1.网页片段
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p></p>
<div>
<button onclick="showToast();">弹框提示</button>
</div>
<p></p>
<script>
function showToast() {
// 调用OC中的showToastWithparams方法
showToastWithparams("参数1","参数2","参数3");
}
</script>
</body>
</html>
2.主要代码
- (void)viewDidLoad {
[super viewDidLoad];
// 1.webView
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = self.view.bounds;
webView.delegate = self;
[self.view addSubview:webView];
// 2.加载网页
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test1" withExtension:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"showToastWithparams"] = ^() {
//NSLog(@"当前线程 - %@",[NSThread currentThread]);
NSArray *params = [JSContext currentArguments];
for (JSValue *Param in params) {
NSLog(@"%@", Param); // 打印结果就是JS传递过来的参数
}
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js调用oc原生代码成功!"] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
});
//JSValue *this = [JSContext currentThis];
};
}
3.测试结果
由于在HTML中,添加一个事件有两种写法,我上面所使用的<button onclick="showToast();">弹框提示</button>
为第一种,下面我简单说说第二种<button onclick="objective.showToast('参数1','参数2');">弹框提示</button>
。
- 网页片段(由于楼主不会JS,所以网页写得超级简单 只有一个按钮。。。。。)
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p></p>
<div>
<button onclick="objective.showToast('参数1','参数2');">弹框提示</button>
</div>
<p></p>
</body>
</html>
2.代码片段
2.1 定义TestJSExport协议,该协议继承JSExport,协议里面定义的方法即是供JS调用的OC原生方法
/** JSExportAs的作用是用showToastWithParameters的OC方法来作为JS方法showToast的别名*/
@protocol TestJSExport<JSExport>
JSExportAs
(showToast,
- (void)showToastWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo
);
@end
2.2 在.m文件里面关联JS 和OC
@interface ViewController ()<UIWebViewDelegate,TestJSExport>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1.webView
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = self.view.bounds;
webView.delegate = self;
[self.view addSubview:webView];
// 2.加载网页
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test1" withExtension:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
}
#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// // 打印异常
// context.exceptionHandler =
// ^(JSContext *context, JSValue *exceptionValue)
// {
// context.exception = exceptionValue;
// NSLog(@"异常消息 - %@", exceptionValue);
// };
// 用TestJSExport协议来关联关联objective的方法
context[@"objective"] = self;
}
#pragma mark -- TestJSExport协议
- (void)showToastWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo {
NSLog(@"当前线程 - %@",[NSThread currentThread]);// 子线程
//NSLog(@"JS和OC交互 - %@ -- %@",parameterone,parametertwo);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js调用oc原生代码成功! - JS参数:%@,%@",parameterone,parametertwo] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
2.3 由于- (void)showToastWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo
这个方法是在子线程做事情,我们在里面弹框就会报如下错误:
只要把弹框放到主线程进行就ok了:
#pragma mark -- TestJSExport协议
- (void)showToastWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js调用oc原生代码成功! - JS参数:%@,%@",parameterone,parametertwo] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
});
}
2.4 测试结果: