美文网首页面试iOS菜鸟到大神ios
JavaScript:浅谈iOS与H5的交互-JavaScrip

JavaScript:浅谈iOS与H5的交互-JavaScrip

作者: 神经骚栋 | 来源:发表于2016-11-15 09:12 被阅读5614次

    </br>

    前言


    小的作为一个iOS程序猿,可能研究JavaScript以及H5相关的知识并不是为了真正的要去转行做这一方面,其实更多的为了要研究OC中的JavaScriptCore框架,JavaScriptCore框架主要是用来实现iOS与H5的交互.当然了,最早我曾经写过iOS浅谈webView的JS一种用法.(去掉web显示页的广告),这种算得上最简单的iOS与H5的交互了.现在混合编程越来越多,H5的相对讲多,所以研究JavaScriptCore框架是相当有必要的.好了不扯这些没用的了,来搞今天的主题---JavaScriptCore框架,首先来说说JavaScriptCore框架几个常用的类.

    </br>

    JavaScriptCore的类说明


    在做OC与H5的交互之前,我们需要先导入JavaScriptCore框架.

    #import <JavaScriptCore/JavaScriptCore.h>
    

    然后我们进入JavaScriptCore框架的JavaScriptCore.h,我们发现总共如下有几个类.

    • JSContext

    JSContext代表一个JavaScript的执行环境的一个实例。所有JavaScript执行都是在上下文内进行。JSContext还用于管理对象的生命周期内JavaScript的虚拟机(API文档原文翻译)。作为上下文很多地方我们的都有可能使用过,比如CoreData,CoreGraphics等等,那么上下文对象到底是什么呢?你可以理解为是一个两者的连接桥梁,如图所示.


    </b>

    • JSValue

    JSValue的主要作用就是用来接收JSContext执行后的返回结果,当然了,JSValue可以是js的任意类型,例如,js中的变量,对象以及函数.下面我们就举个例子说明一下,例如我们现在需要从JSContext对象中接收到一个变量.我们可以如下所示.
    </b>
    首先在<script>标签中我们定义了一个字符串变量.

        <script type="text/javascript">
            var myObject = "myObject";
        </script>
    

    然后我们在OC的代码中如下表示,其中self.context代表着已经初始化完成的JSContext对象.

        JSValue *myObject = self.context[@"myObject"];
    

    当然了,我们如果需要在OC中使用JSValue对象,那么我们可能需要适当的转换一下他的类型,框架提供了很多方法.比如上面的例子我们想要打印的话就可以如下所示.

        NSLog(@"%@",[myObject toString]);
    

    </br>

    • JSManagedValue

    JSManagedValue这个类其实到现在我还没有具体用到过.当时这里我也要根据API文档说明一下.JSManagedValue是JSValue的封装,用它可以解决JS和OC代码之间循环引用的问题,JSManagedValue最常用的用法就是安全的从内存堆区里面引用JSValue对象.如果JSValue存储在内存的堆区的方式是不正确的,很容易造成循环引用,然后导致JSContext对象不能正确的释放掉.

    </br>

    • JSVirtualMachine

    一个JSVirtualMachine对象其实代表着一个JavaScript对象空间或者一组执行资源。JSVirtualMachine支持线程安全锁,虚拟机,并发分配支持的JavaScript执行.也就说JSVirtualMachine用来管理整个JavaScript,当然了,这个类我也没有用到过.

    </br>

    • JSExport

    JSExport是一个协议,通过实现它可以完成把一个native对象暴漏给JS,当然了我们要指定native对象.比如我们指定native对象就是自身.如下所示.

        self.context[@"native"] = self;
    

    </br>

    JavaScriptCore中JSContext对象的初始化


    在进行iOS与H5的交互之前,我们需要做的是对JSContext对象进行初始化的设置,全程是在webView的*- (void)webViewDidFinishLoad:(UIWebView )webView这个代理方法中进行的.所以我们需要对webView进行初始化以及设置代理,这里就不过多的言语了.我们直接看一下在代理方法中如何对JSContext对象进行初始化设置.代码如下所示.

    - (void)webViewDidFinishLoad:(UIWebView *)webView{
    
         //对JSContext对象进行初始化
         self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
             //验证JSContext对象是否初始化成功
         self.context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue)
        {
            context.exception = exceptionValue;
        };
    }
    

    </br>

    JavaScript调取OC代码


    上一个模块我们已经对JSContext对象进行了初始化,那么要使用JavaScript调取OC代码主要有两种方式,一种是使用block(简单粗暴);另外一种是使用JSExport协议(决胜千里).

    • block方式

    block方式比较简单,也是我比价推荐的一种,但是要注意防止循环引用问题的发生.首先我们先说一下不带有参数的函数调用,也就是说我们不需要从网页那边有参数的传值,比如跳转页面等等.代码如下所示.
    </b>
    HTML文件中的<body>标签的按钮代码

                <button onclick="myAction();" style="">点击按钮返回上一个页面</button>
    

    OC中在- (void)webViewDidFinishLoad:(UIWebView *)webView方法中对block块进行代码实现.

        __weak typeof(self)temp = self;
        self.context[@"myAction"] = ^(){
            
            [temp.navigationController popViewControllerAnimated:YES];
            
        };
    

    接下来我们看一下,通过页面的传值,我们把H5标签的值作为参数进行传值操作,并且调用OC的block进行打印.
    </b>
    HTML文件中的<body>标签的div标签代码(一个输入框,一个按钮)

                <input type="text" name="输入框" id="textField" placeholder="请输入要打印的信息" />
    
                <button onclick="log(document.getElementById('textField').value);">打印输入框信息</button>
    

    然后我们在OC的代码中实现以下block函数.其中string参数就是网页中输入框的值.

        self.context[@"log"] = ^(NSString *string){
        
            NSLog(@"%@",string);
        
        };
    

    效果图如下所示.坐标是手机模拟器,右边是Xcode控制台.


    • JSExport协议方式

    通过实现JSExport协议方式进行OC与JS的交互,这里我只是简单的实现以下没有参数的函数调用.首先,我们在HTML文件中创建一个按钮用来调用OC中JSExport协议方法.
    </b>

                <button onclick="native.myLog();">调用OC中myLog方法</button>
    

    我们需要把native对象指定为当前控制器,也就是自己.这句代码是在- (void)webViewDidFinishLoad:(UIWebView *)webView方法中实现的.

        self.context[@"native"] = self;
    

    然后我们要写一个继承于JSExport的协议,协议如下所示.这里我要做一下对JSExportAs(PropertyName,Selector)这个宏定义做一下解释,也就说在PropertyName代表着JS函数的名称,而Selector代表着OC的协议方法,要注意的是Selector必须要有参数,不管是否需要传值.(不知所以然.😭,求大神讲解)

    @protocol WebExport <JSExport>
    JSExportAs
    (myLog ,
     - (void)myOCLog :(NSString *)string
     );
    @end
    

    定义好了方法之后,我们就让当前的控制器遵守协议.实现代理方法.

    @interface JavaScriptCallOCViewController ()<UIWebViewDelegate,WebExport>
    
      - (void)myOCLog :(NSString *)string{
        NSLog(@"你好,世界!");
    }
    

    </br>

    OC调用JavaScript函数


    iOS浅谈webView的JS一种用法.(去掉web显示页的广告)中我们说过一种比较简单的通过OC调取JavaScript函数的方式,当然了,我们就是使用stringByEvaluatingJavaScriptFromString方法添加了一个JS语句.代码如下所示.具体可以查看原文.

    - (void)webViewDidFinishLoad:(UIWebView *)webView{ 
    
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.getElementsByClassName('adpic')[0].style.display = 'none'"];
    
    }
    

    今天我们说一下如何使用JavaScriptCore让OC调用JavaScript函数,使用JavaScriptCore进行OC调用JavaScript函数是很容易进行传值操作的.首先我们需要在HTML文件创建爱你一个带有id的标签以及一个JavaScript函数.代码如下所示.

                <b id="label">需要改变的标签</b>
    
    
        <script type="text/javascript">
            function labelAction(obj) {
    
                document.getElementById('label').innerHTML = obj;
    
            }
        </script>
    
    

    然后就是OC代码中的操作了,我们依然先需要对webview和JSContext对象进行初始化设置.这里就不再重复的啰嗦了.
    为了演示的效果,我把函数的调用放在了rightBarButtonItem的调用方法中.整体的代码如下所示.

        self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"改变" style:UIBarButtonItemStyleDone target:self action:@selector(changeWebTxet)];
    

    下面就是实现方法,首先我们要先通过JSContext对象获取到JS中的对应函数并且使用JSValue对象进行接收.然后我们通过使用- (JSValue *)callWithArguments:(NSArray *)arguments;进行JS函数的调用,当然了这里的JS函数没有返回值,我也就没有做接收返回值的工作.

    -(void)changeWebTxet{
        JSValue *labelAction = self.context[@"labelAction"];
        [labelAction callWithArguments:@[@"你好,JS世界!"]];
    }
    

    我们看一下整体的实现效果.

    </br>

    尾声


    JavaScriptCore框架的初步试用个人感觉还是挺好的,JavaScriptCore框架上手比较简单.当然了,对JavaScript还要要有一定理解和基础的,这样学习起来更简单一些.如果有什么问题欢迎来跟骚栋探讨,最后附上本文的实现Demo.

    JavaScriptCore框架Demo传送门🚪

    </br>


    相关文章

      网友评论

      • 潇湘候晨雪:请问大神,要是我这边(OC)传值给H5要怎么做呢?线程间关系和类型关系怎么处理~~?jijiji
        潇湘候晨雪:@神经骚栋 我这边的问题就是线程间关系,一个异步的网络方法~~等H5方法跑完了,再进我数据请求的方法!
        神经骚栋:@潇湘候晨雪 线程间没太了解,但是关于怎么OC传JS你可以通过下载Demo 比对文章中最后一部分,很简单,注意JS context循环引用问题就好
      • 9b27b67eccb4:输入完“请输入要打印的信息”,然后点击“点击按钮返回上一个页面”,直接闪退。博主大神,解答一下~
        dbc4afc32bb6:dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.navigationController popViewControllerAnimated:YES];
        });
      • 小怡情ifelse:涨姿势了
      • 困惑困惑困惑:如何把oc里面的参数传到h5页面呢???
        神经骚栋:@困惑困惑困惑 是的
        困惑困惑困惑:@神经骚栋能否加下我的扣扣,245501373,详细请教下呢,大神
        神经骚栋:@困惑困惑困惑 请看博客,博客中有写~
      • 山是水的故事:不是特别深入,当你真的用起来的时候,会有一些蛋疼的问题。比如JSContext创建时机的问题
      • 在没老之前:楼主,这样的话,那OC和WEB界面的代码都要协调好才行。如果想和某些已经写好了的网页交互,方法和id都不是我们能控制的,那怎么方便的交互呢
        神经骚栋:@在没老之前 其实,在实际中更多的是移动端与前端人物进行交流商定的,并不是所有的工作都是移动端这边完成的。。
      • 我呃呃呃呃呃:有地方说这句话使用了私有属性,审核会不通过
        是真的吗
        self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        神经骚栋:@我呃呃呃呃呃 没有。。。并没有项目。。。
        我呃呃呃呃呃:@神经骚栋 那你的用了这个上线了没
        神经骚栋: @我呃呃呃呃呃 应该是没有问题的
      • 回不去的是故乡:请问 wkwebview可以获取到context吗?
        神经骚栋:@回不去的是故乡 应该是可以的,并没有测试过。。。:sweat::sweat::sweat:
      • 南坞觉:楼主p了一手好图 :+1:
        叫我小哥哥:大哥 我有个问题你看你能不能帮我想想这个怎么解决, 现在我公司的产品中有这么一个功能,点击签约按钮进入到签约界面进行签约,这个签约界面是一个H5界面,但这个H5界面不是我公司自己的,是用别人合作公司的,然后呢,公司这边要求签约完成后要直接跳转到下一个iOS的原生界面进行后续步骤,那个签约界面签约完成后我是不可能拿到返回数据的,所以就想着个折中的方法,就是让后台自己在添加一个自己的H5界面,签约成功后跳转到自己的这个H5界面,然后通过这个界面调取app的方法进行参数返回,我app这边根据返回这个参数进行判断是否进行界面跳转,问题来了,自己这边的H5在调用我app上的方法时,必须要通过我在app上操作H5界面上的按钮才能给我返回这个参数,然后进行后续操作,安卓那边人家直接签约完成后直接就跳转到了自己的app原生界面,都不用显示自己这边的H5界面,我头大的,实在想不到这个怎么去做。 我想着是在自己这边的H5界面正在进行加载的时候就进行传餐,但是不行啊,必须通过我点击按钮才可以获取参数。我都快懵了,大哥 你能不能帮我想想解决问题。:pray:
        神经骚栋:@南坞觉 哈哈哈
      • Ryannnn:写得不错, 刚开始玩简书, 求大神互相关注
      • iOS_Programmer:
        self.context是什么意思
        神经骚栋:@无他唯手熟尔 JSContext对象 请自行查看Demo
      • 小北风sky:感谢分享
        神经骚栋: @小北风sky 😂😂😂
      • bad48b48c74c:大师
        神经骚栋: @opooc 😂😂😂
      • 一川烟草i蓑衣:好
        神经骚栋: @herohyone 😂😂😂
      • 十二栗子:作者的图片也很炫,请问是自己做的吗?
        神经骚栋:@似水儿流年 嗯嗯。。。没有。。。公司没有美工,所以一些简单的切图作图就都丢给我做了:joy::joy::joy:
        十二栗子:嗯 demo里的图片是自己处理的吧,真是全能大神 哈哈
        神经骚栋:@似水儿流年 是呀,一篇文章有三分之一的时间是用于作图的。。。不过还是自己的图比较方便
      • 淡若烟:再说脏话,我就不理你了
        神经骚栋:@淡若烟 圈粉,就是通过各种方式扩大自己在社交网络上的粉丝群。不管是微博营销,或是打造自媒体,都要建立在拥有大量粉丝的基础上。粉丝,是衡量一个微博帐号价值的基础筹码。在这个“粉丝经济”大行其道的年代,拥有粉丝就是拥有资源。
        淡若烟:@神经骚栋 圈粉是什么
        神经骚栋:@淡若烟 姐姐,你也是来圈粉的。。??。:joy::joy::joy::joy:
      • Thebloodelves:再说脏话我们就py吧
        神经骚栋:@Thebloodelves 直接PY把,别比比别的了~
      • hauibojek:问一下gif制作工具是什么 :grin:
        神经骚栋:@HUswIft 请按照博客的指引。先建立文件路径。
        hauibojek:@神经骚栋 我也用的这个,但是有个问题,就是录屏的范围内我没法进行点击等操作啊。
        神经骚栋:@HUswIft http://www.jianshu.com/p/fded084fa289 这是我写的关于录屏的工具的使用,你可以在里面找到下载链接。。。:smile::smile::smile:
      • YungFan:又在研究这个了啊
      • 下弦月丿:JSExport协议方式 self.context[@"native"] = self 会造成循环引用,更好的方式是注入模型,模型遵循WebExport,实现代理方法
        另外请教下为什么用JSExportAs这个宏,直接写方法不带参数是可以的
        神经骚栋:@下弦月丿 感谢感谢大大~~~~ :smile:
        下弦月丿:@神经骚栋 :joy: 大大不敢称,我也是菜鸟,刚查了下JSExportAs是JS方法和我们的OC方法绑在一起,不用JSExportAs的话,OC代理方法名必须和JS调用方法名一样;注入对象方式可参考http://www.jianshu.com/p/59242a92d4f2,就是加了一个中间层,而不是直接注入self
        神经骚栋:@下弦月丿 感谢大大的回复 JSExportAs这个宏,直接写方法不带参数是可以的 我只是测试了没有参数的使用.当时一直是处于爆红状态,所以现在的我也是知其然,而不知其所以然 我觉得更多的是iOS的规定的写法吧??? 欢迎大大来解答/ 在JavaScriptCore框架我发现一不小心就容易造成循环引用问题. :blush:

      本文标题:JavaScript:浅谈iOS与H5的交互-JavaScrip

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