我爱编程

UIWebView 和 H5 之三 OC 和 JS 之间的相互传

2018-03-11  本文已影响0人  人话博客

当我们建立好 JSContext 和 WebView 的 JS 执行环境的连接关系之后。
我们就可以通过 JSContext 读写 这个 JS 环境的。
主要表现在:
一、 我们可以通过 JSContext 读取 JS 原来的属性 & 函数。

 // 读取 JS 本身的一些函数和变量
    JSValue *jsVar = _jsContext[@"jsVar"];
    JSValue *jsFunc = _jsContext[@"jsFunc"];

二、我们也可以通过 JSContext 通过注入的方式 写入 OC 的方法和变量。

// 往 JSContext 中注入 OC 的数据和方法
    _jsContext[@"ocStr"] = @"我是 OC 的字符串";
    _jsContext[@"ocFunc"] = ^{
        NSLog(@"我是 OC 的方法");
    };

一个比较常见的场景是:

我们会在 OC 里面调用 JS 的方法。
我们会在 JS 里面调用 OC 注入的方法。

都是方法,就有返回值。有返回值,就存在数据传递。

当我们在 OC 中一个带有返回值的 JS 方法时,JS 方法的返回值,就传递到了 OC。
当我们在 JS 中调用一个带有返回值的 OC 方法时,OC 方法的返回值,就传递到了 JS。


场景

15207769160924.jpg

需要实现的效果:

  1. 当往 HTML 文本框里输入一些数据,完毕之后,点击OC 从 JS 获取数据的 UIButton 按钮 。把数据传递到 OC ,然后显示在橙色的 UILabel 上面。
  2. 当在 OC 的 UITextField 中输入用户的昵称和手机之后,需要点击 HTML 中的 从 OC 获取数据,让后把数据显示在 HTML 中的昵称 & Phone 的 span 后面。

分析:

  1. 需要从 JS 获取数据到 OC。数据是来自 JS 的。所以,我们需要在 OC 中调用一个返回数据的 JS 方法。(当然了,JS 里的变量,对象等都可以返回数据)
  2. 需要从 OC 获取数据到 JS。数据肯定是来自 OC 的方法的。所以,我们需要在 JSContext 中注入一个 OC 的方法,并返回数据。让 JS 钩住这个方法,然后再点击了 从 OC 获取数据 的按钮之后,调用这个注入的 OC 方法。

数据来自 JS -->数据来源是 JS 函数 --> JS 函数返回值 --> OC 通过 JSContext 获取 JS 函数,并获取返回数据。

数据来自 OC --> 数据来源是 OC 方法 --> OC 方法返回值 --> OC 需要注入到 JSContext 中,以便 JS 调用 --> JS 调用钩住的 OC 的方法,调用并获取 OC 方法的返回值。


实现目的1:从 JS 获取数据到 OC

从 JS 获取数据到 OC。
潜台词:数据来自 JS -> 来自 JS 函数 -> OC 需要通过 JSContext 找到这个 JS 函数 -> 调用并获取 JS 函数的返回值。

/// 把数据传递给 OC
function sendDataToOC() {
    var jsName = document.getElementById("jsName").value;
    var jsPhone = document.getElementById("jsPhone").value;
    
    return {
        "jsName" : jsName,
        "jsPhone" : jsPhone
    };
}

OC 需要通过 JSContext 来获取到这个 JS 函数,调用并获得返回值。

// 从 JS 获取数据
- (IBAction)getDataFromJSClick:(id)sender {
    // 两种写法。
    // 第一种,对 JS 比较熟练的,可以这么写。
    NSDictionary *dataFromJS = [_jsContext evaluateScript:@"sendDataToOC();"].toDictionary;
    // 第二种,拿到 JSValue 在执行。
    // dataFromJS = [_jsContext[@"sendDataToOC"] callWithArguments:nil].toDictionary;
    
    self.dataFromJSLbl.text = [NSString stringWithFormat:@"jsName:%@ jsPhone:%@",dataFromJS[@"jsName"],dataFromJS[@"jsPhone"]];
}

运行效果:

JS 传递数据到 OC.gif

实现目的2:JS 从 OC 获取数据
JS 从 OC 获取数据。
潜台词:数据来自 OC --> 来自 OC 的方法 --> 需要把这个方法注入到 JSContext,以便让 JS 来调用OC 的方法并获取返回值。

我们需要往 JSContext 中注入一个 OC 的方法(这里表现为 :block),并将数据返回。

 // 把数据传递给 JS。本质上就是让 JS 调用一个返回值的 OC 方法。
    __weak typeof(self) weakSelf = self;
    _jsContext[@"backDataToJS"] = ^{
        __strong typeof(weakSelf) sself = weakSelf;
        NSString *ocName = sself.nickNameTxt.text;
        NSString *ocPhone = sself.phoneTxt.text;
        
        return @{@"name" : ocName , @"18571656584" : ocPhone};
    };

backDataToJS 这个 OC 的方法已经注入到 JSContext 中了。
说白了,就是在当前浏览器的 JS 执行环境中声明了这么一个全局函数。

JS 方面,需要调用这个来自 OC 注入的 JS 函数。(其内部执行的仍然是 OC 的 block,也就是前面一直说的 勾住了)

window.onload = function() {
        // 从 OC 获取数据
        document.getElementById("btn").onclick = function() {
            var dataFromOC = backDataToJS(); // backDataToJS 是 OC 注入到 JSContext 里的 OC 方法 block。
            document.getElementById("nameLabel").innerText = dataFromOC.name;
            document.getElementById("ageLabel").innerText = dataFromOC.18571656584;
        }

    }

运行效果:

OC 传递数据到 JS.gif

最后总结:

  1. 首先让 JS 和 OC 共享一个 JSContext。这样 OC 可以往 JSContext 中注入 OC 的 block。也可以调用 JSContext 中本来已经存在的 JS 函数。同样的 JS 也可以调用 OC 注入的 block。这样的双向调用方法/函数。就为数据传递提供了基础。
  2. 当数据来自 OC 时,说明是 JS 调用 OC 的方法。OC 的方法在 JS 这端表现为,一个全局的 JSFunction。
  3. 当数据来自 JS 时,说明是 OC 调用 JS 函数。通过 JSContext 来获取到这个 JS 函数,并调用。获取来自 JSFunction 的返回。
  4. 于是,数据的双向传递就达成了。
上一篇 下一篇

猜你喜欢

热点阅读