OC与js的交互 - javascriptCore
来源:互联网 发布:传奇数据库下载 编辑:程序博客网 时间:2024/05/17 17:46
前言
鉴于很多小伙伴最近老是问我关于js与OC交互的问题,原本打算先写完CoreAnimation的我,决定先吧这块知识点整理出来,毕竟核心动画的知识的确比较多,不是那么容易写完。
OC与js的交互方式
OC与js的交互方式有好几种,不借助第三方框架的情况下,我们有两个选择,webkit和javascriptCore。
方法一:通过webkit进行OC与js的交互
没认识JavaScriptCore之前,如果想在OC中使用JavaScript代码,一般都是在webview(webview内置webkit引擎,解析JavaScript代码)
主要方法就是在webView的代理中执行: stringByEvaluatingJavaScriptFromString:@"JS的方法名"
这个方法,即可在webView中调用js的函数
同时,通过代理方法webView:shouldStartLoadWithRequest: navigationType:
监听由js在内部定义的类似重定向的消息,并在OC中执行相关的代码
代码如下:
#import "ViewController.h"@interface ViewController ()<UIWebViewDelegate>@property (nonatomic, strong)UIWebView * webView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; self.title = @"Web Test"; [self.view addSubview:self.webView];}-(UIWebView *)webView{ if (!_webView) { _webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds]; _webView.delegate = self; NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@".html"]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:filePath]]; [_webView loadRequest:request]; } return _webView;}- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{// 判断js是否需要OC调用相关的方法. if ([request.URL.absoluteString hasSuffix:@"clickLoginBtn"]) {// 向js传递数据 [_webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"loginCallBack('I am id from app','I am token from app')"]];// 从js获取数据 NSString *webData = [_webView stringByEvaluatingJavaScriptFromString:@"returnData();"]; NSLog(@"%@",webData); [_webView stopLoading]; } return YES;}
js的实现如下:
<!DOCTYPE HTML><html> <head> <script> function loginCallBack(id,token){ var x=document.getElementById("logindata"); alert(id) alert(token) } </script> </head> <body> <h2 id="logindata" align= center>Clict To Transmit Data</h2> <button id="hello" onclick="buttonClick()" >login</button> <script > function buttonClick() { //webview重定向 document.location = "clickLoginBtn" } function returnData(){ return document.getElementById("logindata").script } </script> </body></html>
这种方式可以解决大多数不需要太过复杂的OC与js交互的需求,但对于比较复杂,或者OC与js交互频繁的情况下,更推荐大家使用方法二。
方法二:通过javascriptCore进行OC与js的交互
javascriptCore简介
这是javascriptCore的头文件
在头文件中,我们可以看到,javascriptCore中有5个类,分别是
JSVirtualMachine
JSVirtualMachine顾名思义,是javaScript的虚拟机,是为JSContext提供运行资源。JSManagedValue
主要是作为一个引用桥接,将 JSValue 转为 JSManagedValue 类型后,可以添加到 JSVirtualMachine 对象中,这样能够保证你在使用过程中 JSValue 对象不会被释放掉,当你不再需要该 JSValue 对象后,从 JSVirtualMachine 中移除该 JSManagedValue 对象,JSValue 对象就会被释放并置空。JSContext
JSVirtualMachine为JavaScript的运行提供了底层资源,JSContext就为其提供着运行环境。通过- (JSValue )evaluateScript:(NSString )script;方法就可以执行一段JavaScript脚本,并且如果其中有方法、变量等信息都会被存储在其中以便在需要的时候使用。而JSContext的创建都是基于JSVirtualMachine:- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,如果是使用- (id)init;进行初始化,那么在其内部会自动创建一个新的JSVirtualMachine对象然后调用前边的初始化方法。JSValue
JSValue则可以说是JavaScript和Object-C之间互换的桥梁,它提供了多种方法可以方便地把JavaScript数据类型转换成Objective-C,或者是转换过去。JSExport
JSExport是一个协议,让JSContext运行环境中的JavaScript 可以识别该协议中定义的实例方法、类方法、属性等,让objective-c/swift与JavaScript能够自动交互;
通过OC执行js方法或调取js属性。
下面通过JSContext的evaluateScript方法模拟执行js代码,写一些简单的demo。
例子:
JSContext *context = [[JSContext alloc] init];[context evaluateScript:@"var arr = [1, 2, 'This is js string'];var sum = function(a, b) { return a+b;}"];JSValue *jsArray = context[@"arr"];JSValue *jsSum = context[@"sum"];JSValue *jsSumResult = [jsSum callWithArguments:[NSArray arrayWithObjects:@12, @33, nil]];
示例代码里的,JSValue *jsArray,jsArray对应着javaScript中的一个 array对象:arr。所以我们可以对jsArray进行一些操作,从而操作javaScript 中的 “arr”。
例如:
jsArray[0];//1
jsArray[2];//This is js string
jsArray[1] = 49;//修改arr 的第二个元素。
jsArray[@”length”];//结果是3,调用js arr对象的方法。
又比如我们示例代码里的,jsSum,对应着sum function,因此我们可以通过操作jsSum从而调取javaScript中的 “sum function”。
JSValue *jsSumResult = [jsSum callWithArguments:[NSArray arrayWithObjects:@12, @33, nil]];
如同这个例子,我们可以方便的通过JSValue对象的 callWithArguments:方法来直接调取 js 的 function。js function的多参数,在OC中,由NSArray组装而成。
通过js执行OC方法或调取OC属性。
有两种方式可以方便的通过js 调用 OC:
Block 用来调用方法。
我们有一个OC方法,提供给js调用- (NSInteger)sumWithA:(NSInteger)a B:(NSInteger)b C:(NSInteger)c{return a + b + c;}- (void)jsToOcFunction{//需要写的代码JSContext *context = [[JSContext alloc] init];context[@"sumNums"] = ^(NSInteger a, NSInteger b, NSInteger c) { return [self sumWithA:a B:b C:c];};//模拟执行js代码JSValue *sum = [context evaluateScript:@"sumNums(7, 56, 22)"];NSLog(@"sum %@", sum);//sum 85}
Block方式需要特别注意的是:
- 不论在任何情况下,不要在Block中直接使用外面的JSValue对象, 而应该把JSValue当做参数来传进Block中。
- 不论在任何情况下,不要在Block中直接使用外面的JSContext对象, 而应该使用 [JSContext currentContext]获取。
JSExport protocol 用来调用对象。
使用rumtime 为一个系统控件UIButton增加JSExport protocol@protocol UIButtonExport <JSExport>- (void)setTitle:(NSString *)title forState:(UIControlState)state;@end- (void)changeTitle{class_addProtocol([UIButton class], @protocol(UIButtonExport));UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];[button setTitle:@"你好 OC" forState:UIControlStateNormal];button.frame = CGRectMake(100, 100, 100, 100);[self.view addSubview:button];JSContext *context = [[JSContext alloc] init];context[@"button"] = button;[context evaluateScript:@"button.setTitleForState('你好 js', 0)"];}
通过runtime的方式增加JSExport protocol之外,还可以通过category的方式,比如:
//UIButton+js.h#import <UIKit/UIKit.h>#import <JavaScriptCore/JavaScriptCore.h>@protocol UIButtonExport <JSExport>@property (nonatomic,assign) int index;- (void)setTitle:(NSString *)title forState:(UIControlState)state;@end@interface UIButton (js) <UIButtonExport>@property (nonatomic,assign) int index;- (void)setTitle:(NSString *)title forState:(UIControlState)state;@end
可以看到,如果想要在js中调用OC 的类或者对象的方法,需要将方法在JSExport protocol中声明。
当然上面这些只是一些演练,我们在实际操作中会有些不同。
javascriptCore 实战
在最近的斑马王国2.0的app中,支付中有优惠券的使用,而优惠券是放在H5进行实现的,有个简单的OC与js的交互。
当用户点击app中的优惠券时,打开优惠券的网页,在优惠券页面中点击对应的优惠券,则将对应的优惠券选中,传回app并跳转回支付页面。
在这次的开发中,我使用了block的方式进行了实现。实现过程中,我发现JSContext的这个上下文似乎应该从页面获取,经过打印webView的相关信息,发现我们可以通过这样的方式获取javascriptContext: [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
下面是在优惠券的h5页面声明OC方法的代码:
- (void)webViewDidFinishLoad:(UIWebView *)webView{ NSLog(@"加载成功"); if (_jsContext == nil) { // 1. _jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 2. 关联打印异常 _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) { context.exception = exceptionValue; DDLogVerbose(@"异常信息:%@", exceptionValue); }; //声明js调用的OC方法 __weak ZBWebViewController *weakSelf = self; //选择优惠券代码 _jsContext[@"selectCoupons"] = ^(NSString *couponsName, NSString *couponsCode, int selected) { return [weakSelf selectCoupons:couponsName couponsCode:couponsCode selected:selected]; }; //跳转立即体验代码 _jsContext[@"gotoExperience"] = ^(){ return [weakSelf gotoExperience]; }; }}
我在网页加载成功的时候,来获取这个准确的JSContext,同时声明了我们将要在JS中调用的OC方法,并进行了异常信息的打印。
下面是OC中执行的优惠券选择代码:
/** * 选择优惠券 * * @param couponsName 优惠券名称 * @param excCode 优惠券码 * @param selected 是否选中优惠券 */- (void)selectCoupons:(NSString *)couponsName couponsCode:(NSString *)excCode selected:(int)selected{ NSLog(@"-- 选择优惠券 ---couponsName:%@---disCode:%@---selected:%d",couponsName,excCode,selected); \\执行跳转并传值。}
而在JS中,我们只需要在选中优惠券的时候,selectCoupons(couponsName,excCode,select);
这样调用即可。
总结
通过javascriptCore,我们可以随意的在JS与OC中进行切换。
1、在OC/swift里,所有JavaScript代码都可以在JavaScript运行环境(JSContext)中通过evaluateScript运行;
2、在OC/swift里,所有JavaScript中的方法、对象、属性都可以通过objectForKeyedSubscript(类似字典的方式,context.objectForKeyedSubscript("Person") == context[@"Person"]
,上面的演示都用的是后者)来取得,取得所有对象均为JSValue类型
3、通过objectForKeyedSubscript取得的JavaScript中的对象,都遵循该对象在JavaScript中有的所有特性,如数组的长度,无数组越界,自动延展的特性
4、通过objectForKeyedSubscript取得的JavaScript中的方法,均可以通过callWithArguments传入参数调用JavaScript中的方法并返回正确的结果(类型仍然为JSValue,可以通过toObject的方式转化为对应的OC对象)
5、补充一点:除了通过objectForKeyedSubscript取得JavaScript对象外,我们也可以通过 setObjectForKeyedSubscript的方式给JavaScript中传递类型或对象(传递类型:context[@”Book”] = [Book class];传递对象:context[@”book”] = book),传递完成的对象或者类型,可以在JavaScript中直接使用,类似button.setTitleForState('你好 js', 0)
6、我们可以通过继承JSExport的中自定义协议(@protocol)(定义的属性、方法)的方式,让给任意类型添加可以在JavaScript中访问到的属性、方法,方便JS直接进行点操作
- OC与js的交互 - javascriptCore
- OC与JS交互---JavaScriptCore
- 使用javascriptcore实现oc与js的交互
- iOS OC与JS的交互(JavaScriptCore实现)
- IOS中 使用JavaScriptCore 实现OC与JS的交互
- IOS中 使用JavaScriptCore 实现OC与JS的交互
- OC与JS交互之JavaScriptCore
- OC与JS交互之JavaScriptCore
- OC与JS交互之JavaScriptCore
- OC和JS交互 -- JavaScriptCore
- iOS webView的高级用法之JS交互,js与oc的相互调用(JavaScriptCore)
- OC与JS交互(JavaScriptCore框架入门介绍)
- iOS开发:JavaScriptCore.framework的简单使用--JS与OC的交互篇
- JS与OC交互(iPhone项目)中JavaScriptCore.framework的使用
- iOS 基于JavaScriptCore 不等webView加载完毕就交互,网页获取原生内容。 webView的高级用法之JS交互,js与oc的相互调用
- 基于JavaScriptCore的OC与JS互相调用
- OC与JS的交互
- JS与OC的交互
- 上传本地项目到github
- response.setHeader各种用法
- Appium的一点一滴:Xpath(2.0) 正则函数
- 使用MYCAT轻松实现MYSQL水平分片
- Java,MD5
- OC与js的交互 - javascriptCore
- IE浏览器请求json数据弹出下载框问题【在springmvc中配置如下内容,可以避免】
- 在用户空间程序也使用container_of()
- 双边滤波C#及Unity3D龟速实现
- leetcode--Best Time to Buy and Sell Stock III
- linux 文件名命名规则
- 【Android】app打包成apk文件以后,如何查看VersionCode、VersionName等版本信息
- MBTI职业性格测试
- linux命令nohup的使用,ps-aux