iOS下JS与OC互相调用(四)--JavaScriptCore
1、简要介绍JavaScriptCore
JavaScriptCore
是一个iOS 7 新添加的框架,使用前需要先导入JavaScriptCore.framework
。
然后我们在JavaScriptCore.h
中可以看到,该框架主要的类就只有五个:
1.1 JSVirtualMachine JSVirtualMachine
看名字直译是JS 虚拟机,也就是说JavaScript是在一个虚拟的环境中执行,而JSVirtualMachine
为其执行提供底层资源。
通常情况下我们一般都这样创建JSContext:
// 通过webView的获取JSContext。 JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
OC、JSValue、JavaScript的类型对应关系表:
JS调用OC分两种情况
一,js里面直接调用方法(Block方式)
二,js里面通过对象调用方法(JSExport协议方式)
先说第一种吧:
创建UIWebView:
self.webView = [[UIWebView alloc] initWithFrame:self.view.frame]; self.webView.delegate = self; NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil]; // NSURL *htmlURL = [NSURL URLWithString:@"http://www.baidu.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL]; // 如果不想要webView 的回弹效果 self.webView.scrollView.bounces = NO; // UIWebView 滚动的比较慢,这里设置为正常速度 self.webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; [self.webView loadRequest:request]; [self.view addSubview:self.webView];
HTML的内容也大致一样,不过JS的调用有些区别,更简单了。
function shareClick() { share('测试分享的标题','测试分享的内容','url=http://www.baidu.com'); } function shareResult(channel_id,share_channel,share_url) { var content = channel_id+","+share_channel+","+share_url; asyncAlert(content); document.getElementById("returnValue").value = content; } function locationClick() { getLocation(); } function setLocation(location) { asyncAlert(location); document.getElementById("returnValue").value = location; }
添加JS要调用的原生OC方法
#pragma mark - UIWebViewDelegate - (void)webViewDidFinishLoad:(UIWebView *)webView { NSLog(@"webViewDidFinishLoad"); [self addCustomActions]; }
将所有要添加的功能方法,集中到一个方法addCustomActions
中,便于维护
#pragma mark - private method - (void)addCustomActions { JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; [self addScanWithContext:context]; [self addLocationWithContext:context]; [self addSetBGColorWithContext:context]; [self addShareWithContext:context]; [self addPayActionWithContext:context]; [self addShakeActionWithContext:context]; [self addGoBackWithContext:context]; }
然后每一个小功能独立开来,这样修改和解决Bug的时候能够快速定位到某个功能
- (void)addShareWithContext:(JSContext *)context { __weak typeof(self) weakSelf = self; context[@"share"] = ^() { NSArray *args = [JSContext currentArguments]; if (args.count < 3) { return ; } // 这个地方取到的值是jsvalue,需要转化一下 NSString *title = [args[0] toString]; NSString *content = [args[1] toString]; NSString *url = [args[2] toString]; // 在这里执行分享的操作... // 将分享结果返回给js NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url]; [[JSContext currentContext] evaluateScript:jsStr]; }; }
OC调用JS方法就有多种方式了。首先介绍使用JavaScriptCore框架的方式。
方式1
使用JSContext的方法-evaluateScript
,可以实现OC调用JS方法。
下面是一个调用JS中payResult
方法的示例代码:
NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"]; [[JSContext currentContext] evaluateScript:jsStr];
方式2
使用JSValue的方法-callWithArguments
,也可以实现OC调用JS方法。
下面这个示例代码依然是调用JS中的payResult
:
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; [context[@"payResult"] callWithArguments:@[@"支付弹窗"]];
当然,如果是在执行原生OC方法之后,想要在OC执行完操作后,将结果回调给JS时,可以这样写:
- (void)addPayActionWithContext:(JSContext *)context { context[@"payAction"] = ^() { NSArray *args = [JSContext currentArguments]; if (args.count < 4) { return ; } NSString *orderNo = [args[0] toString]; NSString *channel = [args[1] toString]; long long amount = [[args[2] toNumber] longLongValue]; NSString *subject = [args[3] toString]; // 支付操作 NSLog(@"orderNo:%@---channel:%@---amount:%lld---subject:%@",orderNo,channel,amount,subject); // 将支付结果返回给js // NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"]; // [[JSContext currentContext] evaluateScript:jsStr]; //自动回调 [[JSContext currentContext][@"payResult"] callWithArguments:@[@"支付成功"]]; }; }
方式3 利用UIWebView的API:
NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"]; [self.webView stringByEvaluatingJavaScriptFromString:jsStr];
第一种方式到此介绍完毕.
下面介绍第二种<JSExport>协议的方式:
首先我们新建一个类,JSNativeMethod:
.h 声明
#import <Foundation/Foundation.h> #import <JavaScriptCore/JavaScriptCore.h> //首先创建一个实现了JSExport协议的协议 @protocol TestJSObjectProtocol <JSExport> //此处我们测试几种参数的情况 - (NSString *)imgCallBack:(NSString *)url; // 通过JSON传过来 - (void)callWithDict:(NSDictionary *)params; @end @interface JSNativeMethod : NSObject<TestJSObjectProtocol> @end
.m实现
@implementation JSNativeMethod - (NSString *)imgCallBack:(NSString *)url { NSLog(@"touch image %@",url); return @"iOS To H5"; } - (void)callWithDict:(NSDictionary *)params { NSLog(@"%@",params); JSValue *jsFunc = self.jsContext[@"uploadimage"]; [jsFunc callWithArguments:@[@{@"image":@"image upload success"}]]; } @end
然后在WebView代理方法里:
#pragma mark - UIWebViewDelegate - (void)webViewDidFinishLoad:(UIWebView *)webView { // 获取当前JS运行环境 self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; / JSNativeMethod *call = [[JSNativeMethod alloc] init]; //将JSNativeMethod封装到JavaScript函数Native()中 self.jsContext[@"Native"] = call; //JSContext 还有另外一个有用的招数:通过设置上下文的 exceptionHandler 属性,你可以观察和记录语法,类型以及运行时错误。 exceptionHandler 是一个接收一个 JSContext 引用和异常本身的回调处理 self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) { context.exception = exceptionValue; NSLog(@"异常信息:%@", exceptionValue); }; }
利用这种方式:需要js那边同样需要Native:
//app端callback callWithDict() function callWithDict(a) { log(JSON.stringify(a)); window.Native.callWithDict(a); }
OK,基本结束,这种方式demo还没整理好,只是在我之前项目做了部分修改,因涉及源码,demo暂时不上,以后补上.
第一种方式:demo地址(Block形式):https://github.com/domanc/JS_OC_JavaScriptCore.git
第二种方式: 待更新!