iOS--hybrid混合开发之 js与OC之间的交互


跟进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 
            #import 
 
            //首先创建一个实现了JSExport协议的协议
            @protocol TestJSObjectProtocol 
 
            //使用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
             
            @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文件
  
  

  

  
  
  
      
       
          
        
              
              
          
                  
          
                      
    
                      
    
  
  
  
  

  
  
  
  
  
  
    
  

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 
#import 

@protocol TestJSObjectProtocol 

///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
    ///这里不需要写什么,只要让这个类遵循上面所规定好的那个协议就好。
@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"]];

你可能感兴趣的:(iOS--hybrid混合开发之 js与OC之间的交互)