• iOS 开发与H5交互(JavaScriptCore框架的使用)


    现在的iOS项目中嵌入了越来越多的Web界面,当然是为了方便,那么为了迎合这一趋势,作为iOS开发程序员,我们必须要了解怎么样用OC去和这些Web界面进行交互。这里介绍的是JavaScriptCore这个框架,他就是苹果为了解决这一问题而推出的框架。

    JavaScriptCore的类说明

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

    #import <JavaScriptCore/JavaScriptCore.h>

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

    #if defined(__OBJC__) && JSC_OBJC_API_ENABLED
    
    #import "JSContext.h"
    #import "JSValue.h"
    #import "JSManagedValue.h"
    #import "JSVirtualMachine.h"
    #import "JSExport.h"
    
    #endif

    1. JSContext

    JSContext 代表一个JavaScript的执行环境的一个实例。所有的JavaScript执行都是在上下文内。作为上下文,我们在很多情况下遇到过,比如CoreData,CoreGraphics等等,那么上下文对象到底是什么呢?你可以理解为是一个两者的连接桥梁,如图所示.

    2.JSValue

    JSValue的主要作用就是用来接收JSContext执行后的返回结果,当然了,JSValue可以是Js的任意类型。例如,JS中的变量,对象,以及函数。下面,我们举个例子来说明一下,例如我们现在需要从JSContext对象中接收到一个变量,我们可以如下所示。

    首先在<script>标签中我们定义了一个字符串变量

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

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

    JSContext 对象的创建有两种方法

    /*使用这个方法进行初始化 系统会自动创建一个JSVirtualMachine对象 然后 调用下面的方法 */
    - (instancetype)init; /*! JSVirtualMachine对象其实代表着一个JavaScript对象空间或者一组执行资源。初始化只需要init 就行了 */ - (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;

    当然 如果你使用了WebView 你可以这样初始化一个JSContext对象

    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    有了JSContext,我们只需要在OC中写出下面的代码,就可以拿到我们在JS中定义的变量了

    JSValue *value = self.context[@"myObject"]; //转换为OC的字符串 NSLog(@"%@",[value toString]);

    3.JSManagedValue

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

    4.JSValueMachine

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

    5.JSExport

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

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

    JavaScript 调取 OC的代码

    那么我们怎么使用JavaScriptCore 进行JS与OC的交互呢,我们先来介绍JS调取OC的代码的方法。主要有两种方法:一种是很简单的直接使用Block,另一种是使用JSExport协议。

    Block方式

    block的方式比较简单,也是我比较推荐的一种,但是要注意防止循环引用问题。首先 我们说一下不带参数的函数调用,也就是我们不需要从网页那边有参数的传值。比如,页面跳转等等,代码如下:

    HTML文件中的<body>标签中的按钮代码

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

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

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

    这样当你点击Web页面上的按钮的时候,就会执行block中的代码。进行页面跳转。

    接下来 我们看一下,通过页面的传值,我们把H5标签的之作为参数进行传值操作。并且调用OC的block进行打印。

    HTML文件中的<body>标签中的代码

    <body bgcolor="antiquewhite">
    
            <!--&lt;!&ndash;打开本地 或者网页&ndash;&gt;-->
            <!--<a href="news.html" target="_top">打开本地</a>-->
            <!--<h1 align="center">标题一</h1>-->
            <!--<h2 class="h2ID">标题2</h2>-->
            <button type="button" onclick="buttonClick('你好','世界')">点击按钮返回上一个页面</button>
            <script type="text/javascript">
                function buttonClick(str1,str2) {
                    alert(str1 + "---" + str2);
                }
            </script>
        </body>

    OC调用这个block中的代码 有两种方法

    - (void)webViewDidFinishLoad:(UIWebView *)webView {
        self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        //第一种方法
    //    self.context[@"buttonClick"] = ^() {
    //        //获取该方法的所有参数
    //        NSArray *args = [JSContext currentArguments];
    //        for (JSValue *arg in args) {
    //            NSLog(@"%@",arg);
    //        }
    //        
    //    };
        //第二种方法
        self.context[@"buttonClick"] = ^(NSString *str1,NSString *str2) {
            NSLog(@"%@ and %@",str1,str2);
        };
    }

    点击按钮 可以看到 打印结果 你好 世界

    JSExport 协议方式

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

    声明一个person类

    .h
    #import <Foundation/Foundation.h>
    #import <JavaScriptCore/JavaScriptCore.h>
    
    
    @protocol webJsExport <JSExport>
    
    - (void) myButtonClick;
    
    
    @end
    
    @interface Person : NSObject<webJsExport>
    
    
    
    @end
    .m
    
    #import "Person.h"
    
    @implementation Person
    
    - (void)myButtonClick {
        NSLog(@"再来一次");
    }
    
    @end

    HTML

    <button type="button" onclick="stu.myButtonClick()">JSExport没有参数</button>

    调用

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.p = [[Person alloc] init];
        self.webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
        self.webView.delegate = self;
        NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
        NSString *path = [[NSBundle mainBundle] pathForResource:@"Index" ofType:@"html"];
        NSString *html = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
        [self.webView loadHTMLString:html baseURL:baseURL];
        [self.view addSubview:self.webView];
    //    self.context = [[JSContext alloc] init];
    //    JSValue *value = self.context[@"myObject"];
    //    //转换为OC的字符串
    //    NSLog(@"%@",[value toString]);
    //    __weak typeof(self)weakSelf = self;
    //    self.context[@"buttonClick"] = ^() {
    //        [weakSelf.navigationController popViewControllerAnimated:YES];
    //    };
    }
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView {
        self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        //第一种方法
    //    self.context[@"buttonClick"] = ^() {
    //        //获取该方法的所有参数
    //        NSArray *args = [JSContext currentArguments];
    //        for (JSValue *arg in args) {
    //            NSLog(@"%@",arg);
    //        }
    //        
    //    };
        //第二种方法
        self.context[@"buttonClick"] = ^(NSString *str1,NSString *str2) {
            NSLog(@"%@ and %@",str1,str2);
        };
        self.context[@"stu"] = self.p;
        
        
    }

    点击JSExport没有参数 会打印:在来一次

    OC调用JavaScript函数

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

    HTML代码

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

    然后 就是OC中的代码了

    下面就是实现方法,首先我们要先通过JSContext对象获取到JS中的对应函数并且使用JSValue对象进行接收.然后我们通过使用- (JSValue *)callWithArguments:(NSArray *)arguments;进行JS函数的调用,当然了这里的JS函数没有返回值,我也就没有做接收返回值的工作.
    
    -(void)changeWebTxet{
        JSValue *labelAction = self.context[@"labelAction"];
        [labelAction callWithArguments:@[@"你好,JS世界!"]];
    }

    通过上面的介绍,我们可以看出来 使用JavaScriptCore 来进行OC与H5 的交互是很方便的,比以前通过WebView的一些方法交互方便多了。

  • 相关阅读:
    Linux 下复制命令行输出内容或直接复制文本内容
    JavaScript Array contrast
    Docker安装 Mysql 8.0 并挂载外部配置和数据
    IPC 方法分类
    Linux 安装各种常用通讯软件
    Docker--关于域名和端口配置问题总结
    Golang--Directional Channel(定向通道)
    数位dp
    STL:reverse函数、upper_bound函数、lower_bound函数
    vue filter中无法访问this的解决方案
  • 原文地址:https://www.cnblogs.com/huanying2000/p/6593937.html
Copyright © 2020-2023  润新知