JavaScriptCoreSafari的一个JavaScript引擎,后在iOS7.0中被变成了iOS的一个框架,用做OC与JS之间的桥接,使得OC与JS交互更加方便简单。
JavaScriptCoreDemo
我们在JavaScriptCore.h
中可以看到如下五个类
//提供OC与JS交互的上下文环境
#import "JSContext.h"
//JS的数据,一般是变量、对象和函数
#import "JSValue.h"
//管理JSValue的内存
#import "JSManagedValue.h"
//一个虚拟资源空间,里面有着JSContext对象,各个JSContext对象可相互传值
#import "JSVirtualMachine.h"
//这是一个协议,可以通过这个协议实现用js调用OC的对象达到JS调用OC的效果
#import "JSExport.h"
JS
先写JS,直接看代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>UIWebView中OC与JS交互</title>
</head>
<style type="text/css">
.button {
background: #f5be14;
margin: 10px;
text-align: center;
width: 300px;
height: 44px;
line-height: 44px;
margin: 10px auto;
border-radius: 5px;
}
#setImage {
width: 200px;
height: 200px;
margin: 0 auto;
}
#setText {
width: 200px;
height: 200px;
margin: 0 auto;
}
</style>
<body>
<div class="button" onclick="firstClick()">今晚打老虎</div>
<div class="button" onclick="secondClick()">请用力的点我</div>
<div class="button" onclick="thirdClick()">弹我弹我</div>
<div class="button" onclick="forthClick()">选择图片</div>
<div class="button" onclick="callOCToCallJSClick()">调用OC执行JS来连接两个字符串</div>
<div id="setImage"></div>
<div id="setText"></div>
</body>
<script type="text/javascript">
var prefixStandard = "LFJSToOC://";
function getText(index) {
return document.getElementsByClassName("button")[index].innerText;
}
function firstClick() {
var action = "firstClick";
var token = getText(0);
alert(jsToOC_Object.getConnectText("我是唐伯虎", token));
}
function secondClick() {
var action = "secondClick";
var token = getText(1);
setUrl(action, token);
}
function thirdClick() {
var action = "thirdClick";
var token = getText(2);
setUrl(action, token);
}
function forthClick() {
var action = "forthClick";
var token = getText(3);
jsToOC_Object.getSystemImage();
}
function callOCToCallJSClick() {
var action = "callOCToCallJSClick";
var token = getText(4);
setUrl(action, token);
}
function setUrl(action, token) {
var url = prefixStandard + "/" + action + "/" + token;
//这个loadUrl就是调用OC的执行函数
loadUrl(url, action, token);
}
function showImageOnDiv(imageStr) {
var imgDiv = document.getElementById("setImage");
imgDiv.innerHTML = "<image style='width:200px;' src='data:image/png;base64,"+imageStr+"'>";
}
//OC调用JS的函数
function ocToJS(str1, str2) {
var str = str1 + " OCTOJS " + str2;
var textDiv = document.getElementById("setText");
textDiv.innerHTML = str;
}
</script>
</html>
实现的效果如下
JS效果图
获取JSContext
我们在webViewDidFinishLoad
代理方法中获取JS的上下文JSContext。
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = title;
NSLog(@"webView加载完成");
//拦截JS的回调
JSContext *jsContext = self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
}
JS调用OC
在JavaScriptCore中,JS调用OC可以有两种方式
1、函数调用
直接在js中调用函数并传递必要的参数即可,js代码如下
//js
loadUrl(url, action, token);
对应的OC代码如下
//注意:这里面是子线程
jsContext[@"loadUrl"] = ^(JSValue *value, NSString *action, NSString *token) {
NSLog(@"value = %@", value);
NSLog(@"action = %@", action);
NSLog(@"token = %@", token);
dispatch_async(dispatch_get_main_queue(), ^{
SEL sel = NSSelectorFromString([NSString stringWithFormat:@"%@:", action]);
objc_msgSend(self, sel, token);
});
};
注意
:因为JSValue
对JSContext
是强引用的关系,所以我们这里要注意不能在block内使用外部的JSValue
对象,会造成循环引用。
JSValue
就是js的数据类型,我们可以根据自己的需要转换成OC类型,JSValue
提供了相应的方法,
- (BOOL)toBool;
- (double)toDouble;
- (int32_t)toInt32;
- (uint32_t)toUInt32;
- (NSNumber *)toNumber;
- (NSString *)toString;
- (NSDate *)toDate;
- (NSArray *)toArray;
- (NSDictionary *)toDictionary;
···
OC的数据类型也可以传唤成JSValue
+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context;
+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context;
+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context;
+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context;
+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context;
···
2、调用OC的对象实现交互
js代码如下
js
jsToOC_Object.getConnectText("我是唐伯虎", token)
这个jsToOC_Object
是我们在OC中创建的对象,getConnectText
是我们在OC中实现的方法,上代码
@protocol JSToOC_Protocol <JSExport, NSObject>
//打开系统相册获取图片
- (void)getSystemImage;
//连接两个字符串
JSExportAs(getConnectText,
- (NSString *)connetText1:(NSString *)str1 withText2:(NSString *)str2);
@end
@interface JSToOC_Object : NSObject <JSToOC_Protocol>
@property (nonatomic, weak) id<JSToOC_Protocol> delegate;
@end
@implementation JSToOC_Object
- (void)getSystemImage {
NSLog(@"JS调用了打开系统相册, 这里走的是子线程,如果对UI操作需要回到主线程");
dispatch_async(dispatch_get_main_queue(), ^{
if (self.delegate && [self.delegate respondsToSelector:@selector(getSystemImage)]) {
[self.delegate getSystemImage];
}
});
}
//连接字符串1和字符串2
- (NSString *)connetText1:(NSString *)str1 withText2:(NSString *)str2 {
NSString *connectText = [NSString stringWithFormat:@"%@,我选择%@", str1, str2];
return connectText;
}
@end
如代码所示,需要定义一个遵守JSExport
的协议,这个协议里面实现的方法就是可以供js直接调用的方法,这里面可以不用JSExportAs
也能直接使用。
然后我们需要在webViewDidFinishLoad
代理里面告诉JSContext我们有这么一个对象专门供js调用OC方法的。
JSToOC_Object *jsToOC_Object = [JSToOC_Object new];
//把OC的JSToOC_Object对象传递给JS,供JS调用OC方法
jsContext[@"jsToOC_Object"] = jsToOC_Object;
JS调用OC后,OC的响应也相对应的有block和JSExport两种方式,他们有个共同点需要注意,回调都是在子线程里。
做完这些,以后只要和前端定义方法名和参数,就能随时调用了。
OC调用JS
1、UIWebView本身可以调用stringByEvaluatingJavaScriptFromString
2、JSContext调用evaluateScript
[self.jsContext evaluateScript:@"showMessage()"];
3、JSValue调用callWithArguments:(NSArray *)arguments;
方法,数组里面是按顺序传的参数
NSString *str1 = @"OC调用JS连接两个字符串";
NSString *str2 = @"试试好不好用";
[self.jsContext[@"ocToJS"] callWithArguments:@[str1, str2]];
效果如下
OC调用JS效果
综上就是JavaScriptCore的基本使用,因为iOS12以后苹果就大力推WKWebView了,所以重心还是要放在WK上,共勉。
网友评论