跟进hybrid混合项目开发已有一段时间了,一直都想着手总结一下js与OC的交互关系,但又感觉一直都还没有摸透,总感觉还差点什么...
下面总结下最近学到的js与OC之间的交互:
简单来说js与OC交互就是通过一些方法使得js可以调用OC中的方法,亦或者OC可以调用js中的方法,使得两者可以互传参数,进行各自处理;下面介绍各自两种方法:
一、OC调用js方法,OC传参数给js:
1.*- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray **)arguments;
这个方法可以让我们可以直接在OC上简单地调用JS上的方法。只是如果定义的方法是全局函数,那么很显然应该在JSContext的glocalObject对象上调用该方法;如果是某JavaScript对象上的方法,就应该用相应的JSValue对象调用。
举个例子,在js上有方法:
function buttonClicke()
{
testobject.TestNOParameter();
}
那么方法“buttonClicke()”则是全局函数;
“TestNOParameter()”则是JavaScript对象上的方法;
//比如:buttonClicke是全局函数,所以用globalObject调用,从OC传递arguments回到web:
[[context globalObject] invokeMethod: buttonClicke withArguments:arguments];
[[context globalObject] invokeMethod:@"CallBack" withArguments:@[@"你好世界",@"a",@"b",@"c"]];
//比如:TestNOParameter是JavaScript对象上的方法,所以用相应的testobject调用,从OC传递arguments回到web:
`?????但这个方法我不知道怎么写,望知道的朋友告知一下////////`
`百度上搜到的全是这段:
JSValue还提供- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;
让我们可以直接简单地调用对象上的方法。只是如果定义的方法是全局函数,那么很显然应该在JSContext的globalObject对象上调用该方法;
如果是某JavaScript对象上的方法,就应该用相应的JSValue对象调用.`
但是我比较笨,不知道所谓的"应该用相应的JSValue对象调用"应该怎么写,请教大家;
2.*- (JSValue *)evaluateScript:(NSString **)script;
这个方法可以简单地在OC上调用js的方法脚本;
比如:
//通过oc方法调用js的alert
[context evaluateScript:@"alert('test js OC')";];
再如js中有方法如下:
function buttonClicke()
{
testobject.alert(value);
}
OC中可以这样调用:前面是先“对象.方法=”,后面再是“方法(回传参数)”;
[context evaluateScript:[NSString stringWithFormat:@"testobject.alert = alert('%@');",@"回传"]];
二、js调用OC方法,js传参数给OC:
1.使用block 直接调用
如果js有方法:
function buttonClicke()
{
Tank("参数");
}
则OC中可以直接使用block响应js的这个Tank方法:
context[@"Tank"] = ^() {
NSArray *arguments = [JSContext currentArguments]; //传过来的是一个数组
NSString *string = arguments[0];
};
//拿到参数后就可以干嘛干嘛了
//或者不用传参数,js那边点击了这个方法,OC这边响应进入到block里,想干嘛就在这里干嘛就是了。
或者js有方法
function clicke()
{
alert(value);
}
OC中可以直接执行并返回值给js用:
context[@"alert"] = ^() {
return @"回传参数";
};
2.使用JSExport 对象调用
在OC中凡事添加了JSExport协议的协议,所规定的方法、变量等就会对js开放,就可以通过js调用到;
通过JSExport协议的重载宏,可以告诉js调用什么方法时,会执行OC端的什么方法;
如js方法为:testobject.TestOneParameter('参数1');
//testobject 是 js 对象,TestOneParameter是方法名,后面为参数,方法名可随意起,没有规范,并不像有些博客上写的要与协议方法拼起来的名字一样,没有必要的。
* 首先创建一个类 继承NSObject并遵循 规定好的这个协议,这个协议遵循JSExport协议:
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
//首先创建一个实现了JSExport协议的协议
@protocol TestJSObjectProtocol <JSExport>
//使用JSExport重载宏JSExportAs来声明方法:
//点进JSExportAs里面看API,可以发现其规则:前面是js的方法如:TestOneParameter;
后面是OC里的这个协议里的方法,(注意后面一定要有一个参数,即使js那边的没有参数,这边也要这样写,大不了接收到的参数为null,这个规则在JSExportAs里写有,note那里);
这一句的意思是告诉js:当执行js端的TestOneParameter方法时(当然,这个方法可以随意其他名字),会调用OC端的“ -(void)TestNOParameter:(id)args);”这个方法,也叫注册
JSExportAs(TestOneParameter, -(void)TestNOParameter:(id)args);
@end
//然后在创建的类遵循上边的协议
@interface EXWebViewBridge : NSObject<TestJSObjectProtocol>
@end
* 然后去.m里实现或者在其他遵循这个协议的类里实现
-(void)TestNOParameter:(id)args
{
NSLog(@"this is ios TestNOParameter = %@",args);
}
* 将这个类创建一个新对象赋给JS的对象
js是通过对象调用的,我们假设js里面有一个对象 testobject 在调用方法
context[@"testobject"]=[EXWebViewBridge new];
//这样就可以了,当JS那边在调用一个方法时,
//如:testobject.TestOneParameter('参数1'),就会来到OC里的协议里使用OC的对象代替JS的对象 并调用对应的协议方法去实现,并传参数过来。
三、下面是代码部分
1,index.html文件
<!--// Created by Tank on 16-10-18.-->
<!--// Copyright (c) 2016年 Tank. All rights reserved.-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<script type="text/javascript" src="index.js"></script>
</head>
<!-- js调用OC 使用block -->
<button id="halle" onclick="buttonClicka()"> Tank</button>
<button id="halle" onclick="buttonClickb()"> 直接reture</button>
<!-- 使用JSExport 对象调用方法 -->
<button id="halle" onclick="buttonClicke()"> 无参</button>
<button id="hallf" onclick="buttonClickf()"> 一参</button>
<button id="hallg" onclick="buttonClickg()"> 两参</button>
<button id="hallg" onclick="buttonClickh()"> 多参</button>
<button id="hallg" onclick="buttonClicki()"> doFoo</button>
</body>
</html>
2,index.js文件
///使用Block js调用OC
function buttonClicka()
{
Tank("参数");
}
function buttonClickb()
{
value = getReture();
alert(value);
}
///使用使用JSExport 对象调用方法 js调用OC
//js这边没有参数
function buttonClicke()
{
testobject.TestNOParameter();
}
function buttonClickf()
{
testobject.TestOneParameter("这是一个参数!")
}
function buttonClickg()
{
testobject.TestOneParameterSecondParameter("这是第一个参数","这是第二个参数!");
}
//多个参数
function buttonClickh()
{
testobject.testMoreParameters("一个","两个","三个","四个");
}
//doFoo
function buttonClicki()
{
testobject.doFoo("one","two");
}
function callBack()
{
//alert(value);
testobject.alert(value);
}
3,创建一个新类TestJSObject.h
//
// TestJSObject.h
// TestJSandOC
//
// Created by Tank on 16/9/20.
// Copyright © 2016年 Tank. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol TestJSObjectProtocol <JSExport>
///Tank:使用这种必须得要带一个参数以上,可以点JSExportAs进里面看;这里说的一个参数是OC这边的方法,即使js那边没有参数传过来,这里的方法也要带一个参数,实现方法那边也要有一个参数,这样才不会报错,如下面这个,js那边没有参数,oc这边还是要有一个参数来接收的。
JSExportAs(TestNOParameter, -(void)TestNOParameter:(id)args);
///js那边有一个参数,Oc这边也要有一个参数接收
JSExportAs(TestOneParameter, -(void)TestOneParameter:(id)args);
///js那边有两个参数,OC这边也要有两个参数接收,当然也可以用一个数组去接收它所有的参数。注意方法名的写法,可以规范成如下,也可以随意,如上所说:
JSExportAs(TestOneParameterSecondParameter, -(void)TestTowParameter:(NSString *)message1 SecondParameter:(NSString *)message2);
///多个参数,用数组接收
JSExportAs(testMoreParameters, -(void)testMorePara:(id)args);
///再如下,告诉双方,当js调用doFoo 时,OC就调用对应的方法“- (void)doFoo:(id)foo withBar:(id)bar);”;
///js传过来的参数会一一对应到OC里的,如果不够或无则显示为null;
JSExportAs(doFoo, - (void)doFoo:(id)foo withBar:(id)bar);
@end
@interface TestJSObject : NSObject<TestJSObjectProtocol>
///这里不需要写什么,只要让这个类遵循上面所规定好的那个协议就好。
@end
4,这个新类的实现TestJSObject.m
//
// TestJSObject.m
// TestJSandOC
//
// Created by Tank on 16/9/20.
// Copyright © 2016年 Tank. All rights reserved.
//
#import "TestJSObject.h"
@implementation TestJSObject
///js那边没有参数,这边也要用一个来接收,接收回来的值为null
-(void)TestNOParameter:(id)message
{
NSLog(@"this is ios TestNOParameter = %@",message);
}
-(void)TestOneParameter:(NSString *)message
{
NSLog(@"this is ios TestOneParameter=%@",message);
}
-(void)TestTowParameter:(NSString *)message1 SecondParameter:(NSString *)message2
{
NSLog(@"this is ios TestTowParameter=%@ Second=%@",message1,message2);
}
///ja传来多个参数,OC使用数组接收
-(void)testMorePara:(id)args
{
NSArray *arguments = [JSContext currentArguments];
NSLog(@"arguments = %@",arguments);
for (JSValue *content in arguments) {
NSLog(@"参数分别是 = %@",content);
}
NSString *stringOne = [[arguments objectAtIndex:2] toString];
NSLog(@"stringOne = 这是第%@参数",stringOne);
}
- (void)doFoo:(id)foo withBar:(id)bar
{
NSLog(@"doFoo = %@,withBar = %@",foo,bar);
}
@end
5,ViewController.m
创建一个webView,并LoadRequest刚才创建的index.html;
在webView的代理方法:webViewDidFinishLoad 下执行:
self.myContext = [self.myWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
///使用对象JSExport
TestJSObject *testJS=[[TestJSObject alloc] init];
///把OC对象 赋给 js对象
self.myContext[@"testobject"]=testJS;
//使用Block
self.myContext[@"Tank"] = ^(){
NSArray *arguments = [JSContext currentArguments];
for (NSString *args in arguments) {
NSLog(@"args = %@",args);
}
//或者
NSString *string = [[arguments objectAtIndex:0] toString];
};
self.myContext[@"getReture"] = ^(){
return @"直接返回一个值给js";
};
///OC调用JS的两个方法:
///直接调用,或者将方法名与参数分开调用
[context evaluateScript:@"alert('test js OC')"]; //如果有变量可以使用stringWithFormat
[[context globalObject] invokeMethod:@"alert" withArguments:@[@"test js OC"]];
网友评论