现在iOS开发中使用的混编情况越来越多了,今天我们就OC与JS的交互来做一下简单的剖析增进了解。我们Demo使用的场景是在webview上展示HTML页面,点击HTML上的按钮进入相册,在相册中选择图片并上传到服务器。我们现在开始进行分析,Demo会在文末给出。
一、OC与JS交互中使用方法注释与关联
首先,我们要导入#import <JavaScriptCore/JavaScriptCore.h>,然后我们来熟悉一下里面常见的几个对象及协议。
1、对象注释:
1、JSContext:给JavaScript提供运行的上下文环境,通过-evaluateScript:方法就可以执行一JS代码
2、JSValue:JavaScript和Objective-C数据和方法的桥梁,封装了JS与ObjC中的对应的类型,以及调用JS的API等
3、JSManagedValue:管理数据和方法的类
4、JSVirtualMachine:处理线程相关,使用较少
5、JSExport:这是一个协议,如果采用协议的方法交互,自己定义的协议必须遵守此协议
2、相互之间的关联:
1、JSVirtualMachine为JavaScript的运行提供了底层资源,
2、JSContext为其提供运行环境,通过- (JSValue *)evaluateScript:(NSString *)script;方法就可以执行一段JS脚本,并且其中有方法变量等信息都会被存储在其中以便在需要的时候使用。而JSContext的创建都是基于JSVirtualMachine:- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,
如果是使用- (id)init;进行初始化,那么在其内部会自动创建一个新的JSVirtualMachine对象然后调用前边的初始化方法。
3、JSValue可以说是JavaScript和OC之间互换的桥梁,它提供了多种
方法可以方便的把JavaScript数据类型转换成Objective-C,或者是转换过去。
知识点链接:http://blog.iderzheng.com/introduction-to-ios7-javascriptcore-framework/
二、HTML内部方法的注释
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div>
<h3>JS与iOS交互</h3>
<h4>JS页面获取iOS系统图片</h5>
</div>
<div>
<input type = "button" style="width: 50%;height: 5%;" id="Button" value="打开相机获取图片" onclick="getIOSImage()"></button><!--按钮的方法getIOSImage -->
</div><dir />
<div>
<img src="testImage.png" id="changeImage"style="width: 30%; height: 30%;" onclick="getIOSImage()"><!--src="图片的相对路径" 如果把html文件导入工程中,图片路径和OC一样只写图片名字和后缀就可以,(记得要先把图片添加到工程) 图片也可以实现按钮的方法getIOSImage -->
</div>
<span id="iosParame" style="width: 200px; height: 50%; color:orangered; font-size:15px" value="等待获取ios参数" >
</div>
<script>
var getIOSImage = function(){//按钮方法getIOSImage中包含代理
var parameter = {'title':'JS调OC','describe':'这里就是JS传给OC的参数'};
// 在下面这里实现js 调用系统原生api
window.iosDelegate.getImage(JSON.stringify(parameter));// 实现数据的 json 格式字符串
}
// 这里是 iOS调用js的方法
function setImageWithPath(arguments){
document.getElementById('changeImage').src = arguments['imagePath'];
document.getElementById('iosParame').innerHTML = arguments['iosContent'];
}
</script>
</body>
</html>
三、webView加载HTML方法的使用
- 处理流程:
1、创建WebView加载本地HTML文件。
2、webView加载完成之后代理中进行JS&OC交互。并调用JS中的代理方法,获取代理中的参数getImage。
3、JS中的按钮连接着代理,只要点击按钮就会调用代理,在代理中有打开相册的的方法beginOpenPhoto
(JavaStript调用本地方法是在子线程中执行的)。
4、在OC选择图片的代理中存储图片并将图片上传到HTML页面中。
5、上传图片到服务器上。
a:设置HTML中的代理以及相关全局变量
@protocol JSDelegate <JSExport>
//这个方法就是window.document.iosDelegate.getImage(JSON.stringify(parameter)); 中的 getImage()方法
- (void)getImage:(id)parameter;
@end
@interface ViewController ()<UIWebViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,JSDelegate>{
int indextNumb;// 交替图片名字
UIImage *getImage;//获取的图片
}
@property(strong, nonatomic) JSContext *jsContext;
@property(retain, nonatomic) UIWebView *myWebView;
@end
b:创建WebView加载本地HTML文件
- (void)setupWebView{
if (!self.myWebView) {
//初始化 WebView
self.myWebView = [[UIWebView alloc] initWithFrame:self.view.bounds];
self.myWebView.backgroundColor = [UIColor colorWithRed:1.000 green:1.000 blue:0.400 alpha:1.000];
// 代理
self.myWebView.delegate = self;
NSURL *path = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
[self.myWebView loadRequest:[NSURLRequest requestWithURL:path]];
[self.view addSubview:self.myWebView];
}
}
c:web加载完成开始监听js的方法
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//1/初始化context
self.jsContext = [self.myWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//2、OC调用JS
//OC用jsContext[@"iosDelegate"] 此种方式捕捉代理
self.jsContext[@"iosDelegate"] = self;//挂上代理 iosDelegate是window.document.iosDelegate.getImage(JSON.stringify(parameter)); 中的 iosDelegate
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){
context.exception = exception;
NSLog(@"获取 self.jsContext 异常信息:%@",exception);
};
}
d:JS中代理调用打开相册的方法
//调用代理,打开相册,然后处理选择的图片 这里可以画出处理的流程
#pragma mark - JSDelegate
- (void)getImage:(id)parameter{
// 把 parameter json字符串解析成字典
NSString *jsonStr = [NSString stringWithFormat:@"%@", parameter];
NSDictionary *jsParameDic = [NSJSONSerialization JSONObjectWithData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding ] options:NSJSONReadingAllowFragments error:nil];
NSLog(@"js传来的json字典: %@", jsParameDic);
for (NSString *key in jsParameDic.allKeys)
{
//将JS传来的JSON字典中key/value全部取出并打印
NSLog(@"jsParameDic[%@]:%@", key, jsParameDic[key]);
}
[self beginOpenPhoto];
}
- (void)beginOpenPhoto
{
// 主队列 异步打开相机
dispatch_async(dispatch_get_main_queue(), ^{
[self takePhoto];
});
}
e:OC点击照片的代理中将图片传给HTML页面展示
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
// 1、判断当前选择的类型是照片
if ([type isEqualToString:@"public.image"])
{
// 2、获取照片并打印照片的大小
getImage = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
NSLog(@"===Decoded image size: %@", NSStringFromCGSize(getImage.size));
// 3、obtainImage 压缩图片 返回原尺寸
indextNumb = indextNumb == 1?2:1;
NSString *nameStr = [NSString stringWithFormat:@"Varify%d.jpg",indextNumb];
//4、存储选中的图片并将图片展示到H5中
[SaveImage_Util saveImage:getImage ImageName:nameStr back:^(NSString *imagePath) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"图片路径:%@",imagePath);
/**5、传值数据到JS中,并展示数据
* 这里是IOS 调 js 其中 setImageWithPath 就是js中的方法 setImageWithPath(),参数是字典
* jsContext[@"setImageWithPath"]为调用js中的方法
* //callWithArguments可以将参数传进去调用方法
*/
JSValue *jsValue = self.jsContext[@"setImageWithPath"];
[jsValue callWithArguments:@[@{@"imagePath":imagePath,@"iosContent":@"获取图片成功,把系统获取的图片路径传给js 让html显示"}]];
});
}];
[picker dismissViewControllerAnimated:YES completion:nil];
}
}
资料来源:https://www.jianshu.com/p/b03a46b47b14
Demo地址:https://github.com/Toxic-YX/JSOCInteraction