Objective-C与JS交互
更新说明
更新记录:
- 2016 年 7 月,第一版。
- 2017 年 8 月,增加OC调用JS方法相关介绍。
JavaScriptCore简介
JavaScriptCore是iOS7引入的新功能,使用JavaScriptCore后可以实现js代码与本地native代码进行相互调用。
要使用JavaScriptCore,首先我们需要引入它的头文件 #import <JavaScriptCore/JavaScriptCore.h>
这个头里面引入了几个重要的对象
#import "JSContext.h"
#import "JSValue.h"
#import "JSManagedValue.h"
#import "JSVirtualMachine.h"
#import "JSExport.h"
- JSContext是JavaScript的运行上下文,他主要作用是执行js代码和注册native方法接口
- JSValue是JSContext执行后的返回结果,他可以是任何js类型(比如基本数据类型和函数类型,对象类型等),并且都有对象的方法转换为native对象。
- JSManagedValue是JSValue的封装,用它可以解决js和原声代码之间循环引用的问题
- JSVirtualMachine 管理JS运行时和管理js暴露的native对象的内存
- JSExport是一个协议,通过实现它可以完成把一个native对象暴漏给js
具体的交互过程可以参见这篇博客https://imciel.com/2016/06/18/oc-js-communication/
OC与JS交互的方式
OC与js交互,主要涉及到两方面:
- OC调用JS方法,将本地JS需要的值传递过去,供JS函数调用;
- JS调用OC的native方法,将JS函数中的返回值传递给本地方法,执行相应操作;
下面将针对以上两条交互方式,展开来说。OC调用JS方法
通过UIWebView展示JS页面,在UIWebView的代理方法中通过执行stringByEvaluatingJavaScriptFromString方法将JS代码执行结果以字符串方式返回,
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *text = aControl.titleLabel.text;
NSString *jsMethod = [NSString stringWithFormat:@"ocScrollToElementByName('%@')", text];
[self.contentView stringByEvaluatingJavaScriptFromString:jsMethod];
}
也可以在当前加载webView页面类的
- (void)viewWillAppear:(BOOL)animated
方法中调用JS方法,执行相关操作,如:
传递参数
// JS交互,传gid,刷新JS页面商品数量
NSString *gid = [[NSUserDefaults standardUserDefaults] objectForKey:@"GoodInfoH5Gid"];
if (gid && gid.length > 0) {
NSString *jsMethod = [NSString stringWithFormat:@"updateGoodNum('%@')",gid];
[_webView stringByEvaluatingJavaScriptFromString:jsMethod];
}
局部刷新列表
// 局部刷新
[_webView stringByEvaluatingJavaScriptFromString:@"updatecartnumAndTotalPay()"];
JS调用OC的native方法
通过JavaScriptCore进行交互,需要在webView的加载完成的代理方法中设置交互上下文-JSContext,并将JS中的交互对象赋给当前类。下面将叙述如何使用JSExport设置引用名称来进行交互,使用JSExport引用名称空间后,对于调用了哪些JS方法就一目了然了。
JSExport引用名称空间交互设置
js那边统一使用一个名为jsObject的对象来调用js的方法进行传值或触发某一特定的事件。在 js 中定义一个方法:
<html>
<head>
<title>Demo</title>
<script type="text/javascript">
function setContent(){
jsObject.shopCartNumChanged(totalNum);
}
</script>
</head>
<body onload="javascript:setContent('ios is: ' + typeof ios)">
</body>
</html>
当点击 js 界面上的一个 “+” 号时,js 那边会查找 OC 代码通过JSContext注册的名为jsObject.shopCartNumChanged的调用方法。现在问题来了,在 OC 中该如何注册该方法呢?答案是使用语言穿梭机—JSExport协议。比如,我有一个 ShopCarViewController 的类。在.h中声明一个名为 MallJSExports 的协议。
// ShopCarViewController.h
#import <JavaScriptCore/JavaScriptCore.h>
@protocol MallJSExports <JSExport>
- (void)shopCartNumChanged:(NSString*)shopNum; //购物车数量变化
- (void)orderGoBackToNative; //订单返回按钮
@end
@interface ShopCarViewController : MallViewController
@end
在.m中当然要声明并实现该协议的方法。
@interface ShopCarViewController()<UIWebViewDelegate,MallJSExports>{
}
@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, copy) NSString *shopNum; //商品数量
@end
@implementation ShopCarViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.title = @"购物车";
_webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, self.view.height-49)];
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:_webUrl]]];
_webView.delegate = self;
_webView.detectsPhoneNumbers = NO;
[self.view addSubview:_webView];
}
#pragma mark - 在webView加载完成的代理方法里设置JSContext
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//JS上下文对象
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"jsObject"] = self;
}
#pragma mark - 购物车数量变化
- (void)shopCartNumChanged:(NSString *)shopNum
{
CLog(@"jsString shopNum = %@",shopNum);
[[NSNotificationCenter defaultCenter] postNotificationName:ShopCartNumIsChanged object:shopNum];
}
#pragma mark - 订单返回按钮
- (void)orderGoBackToNative
{
[self.navigationController popViewControllerAnimated:YES];
}
这里要注意的是 context[@ “jsObject” ] = self 中的的key值是和服务器商量好的, 即, js 中定义的回调方法 jsObject.shopCartNumChanged( )相一致。
在这里对 JSExport 的使用只是简单的设置了一下命名空间,将下标方法暴露给js对象方便服务器调用。其实, JSExport 协议主要用途是把objc复杂对象转换成JSValue并暴露给js对象。 JSExport 作为两种语言的互通协议。 JSExport 中没有约定任何的方法,连可选的(@optional)都没有,但是所有继承了该协议(@protocol)的协议(注意不是Objective-C的类(@interface))中定义的方法,都可以在JSContext中被使用。
补充
关于使用WKWebView进行交互。调用JS函数:
[self.wkwebView evaluateJavaScript:@"refreshList()" completionHandler:^(id _Nullable rr, NSError * _Nullable error) {
}];
参考资料
使用 JavaScriptCore 实现 JS和OC间的通信
JavaScriptCore框架在iOS7中的对象交互和管理
示例代码下载
–EOF–
若无特别说明,本站文章均为原创,转载请保留链接,谢谢