当我们建立好 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。
场景
需要实现的效果:
- 当往 HTML 文本框里输入一些数据,完毕之后,点击OC 从 JS 获取数据的 UIButton 按钮 。把数据传递到 OC ,然后显示在橙色的 UILabel 上面。
- 当在 OC 的 UITextField 中输入用户的昵称和手机之后,需要点击 HTML 中的 从 OC 获取数据,让后把数据显示在 HTML 中的昵称 & Phone 的 span 后面。
分析:
- 需要从 JS 获取数据到 OC。数据是来自 JS 的。所以,我们需要在 OC 中调用一个返回数据的 JS 方法。(当然了,JS 里的变量,对象等都可以返回数据)
- 需要从 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"]];
}
运行效果:
实现目的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;
}
}
运行效果:
最后总结:
- 首先让 JS 和 OC 共享一个 JSContext。这样 OC 可以往 JSContext 中注入 OC 的 block。也可以调用 JSContext 中本来已经存在的 JS 函数。同样的 JS 也可以调用 OC 注入的 block。这样的双向调用方法/函数。就为数据传递提供了基础。
- 当数据来自 OC 时,说明是 JS 调用 OC 的方法。OC 的方法在 JS 这端表现为,一个全局的 JSFunction。
- 当数据来自 JS 时,说明是 OC 调用 JS 函数。通过 JSContext 来获取到这个 JS 函数,并调用。获取来自 JSFunction 的返回。
- 于是,数据的双向传递就达成了。