美文网首页iOS资料iOS调试技巧#iOS#HeminWon
iOS--hybrid混合开发之 js与OC之间的交互

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

作者: 追风筝的荧火虫 | 来源:发表于2016-10-18 15:04 被阅读793次

    跟进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"]];

    相关文章

      网友评论

      • 峰子1994:有demo吗想学习下
        追风筝的荧火虫:@峰子1994 代码部分就是demo了,
      • 2a392233fc62:WebViewJavascriptBridge是支持到iOS6之前的版本,iOS7之后出了JavaScriptCore.framework用于与JS交互,但是不支持iOS6。也有react-native
        Thebloodelves:@noaicooder 嗯,好的,不过这个能做到确不会有人用
        2a392233fc62:@Thebloodelves 用不用就看自己了,我只是说有这几个可以达到交互。个人认为WebViewJavascriptBridge比较好用,也不麻烦,我就是用的这个来做交互的。
        Thebloodelves:@noaicooder react-native这个拿来交互是杀鸡用牛刀了
      • 拿铁加冰:可以尝试用WebViewJavascriptBridge。
        拿铁加冰:@Kiwir_Tank 简书里就有,搜一下就知道了
        追风筝的荧火虫:@拿铁加冰 没用过呢,你有没有demo啊?
      • 414eeb21143a:有没有swift和js或者js和swift相互调用的guide啊?
        追风筝的荧火虫:@19861011 等我学会啊 :joy: ,你也可以研究研究,弄个demo出来大家一起学习 :clap:
        414eeb21143a:@Kiwir_Tank 哎呀 下次是什么时候啊? 期待哦
        追风筝的荧火虫:@19861011 暂时没有哦,下次更新

      本文标题:iOS--hybrid混合开发之 js与OC之间的交互

      本文链接:https://www.haomeiwen.com/subject/vgnzyttx.html