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 <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"]];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 注:本文copy自http://www.jianshu.com/p/ac534f508fb0,纯属当笔记使用。 概...
    BookKeeping阅读 731评论 1 3
  • JavaScriptCore框架主要是用来实现iOS与H5的交互。由于现在混合编程越来越多,H5的相对讲多,所以研...
    水灵芳蕥阅读 1,401评论 1 8
  • 随着H5技术的兴起,在iOS开发过程中,难免会遇到原生应用需要和H5页面交互的问题。其中会涉及方法调用及参数传值等...
    Chris_js阅读 3,064评论 1 8
  • 一、JavaScriptCore常用的类 JavaScriptCore作用:JavaScriptCore是苹果原生...
    CoderZS阅读 904评论 0 8
  • 本文由我们团队的 纠结伦 童鞋撰写。 写在前面 本篇文章是对我一次组内分享的整理,大部分图片都是直接从keynot...
    知识小集阅读 15,241评论 11 172