OC与JS交互
https://www.jianshu.com/p/bb666b71e104
一、简述
目前原生与JS交互的方式有以下几种
JavaScriptCore
WKWebView
拦截URL
WebViewJavascriptBridge库
二、JavaScriptCore
(一)定义
1.JSContext
为JS的执行提供了上下文环境,通过JSCore执行的JS代码都要通过JSContext来执行。(上下文对象给两者的交互搭建了环境)
2.JSValue
是JS值在OC中的封装,以便JS值在OC中执行
3.两者关系
JSValue不可独立存在,必须依存于JSContext。一个JSContext可对应多个JSValue
每一个JSValue对象都要强引用关联一个JSContext。当与某JSContext对象关联的所有JSValue释放后,JSContext也会被释放。
(二)建立连接
1.文件头部引入相关库
#import <JavaScriptCore/JavaScriptCore.h>
2.加载本地HTML页面
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"JSInteraction" ofType:@"html"];
NSString *htmlStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[_webView loadHTMLString:htmlStr baseURL:[NSURL URLWithString:filePath]];
3.在webView的代理方法webViewDidFinishLoad中实现连接
//通过KVC方式从webView上获取相应的JSContext
self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//设置错误处理函数
[self.context setExceptionHandler:^(JSContext *context, JSValue *exception) {
NSLog(@"oc catches the exception: %@",exception); }];
self.context[@"JsInteractive"] = self;
Pay:由于加载JS在webViewDidFinishLoad()之前,故JS无法调用OC方法。
解决方法:
1)可在JS中构建一个事件 “webViewDidFinishLoad()”,在原生的webVIewDidFinishLoad()方法中通过context的evaluateScript方法或者webView的stringByEvaluatingJavaScriptFromString()方法来调用。
[self.context evaluateScript:@"webViewDidFinishLoad()"];
[webView stringByEvaluatingJavaScriptFromString:@"webViewDidFinishLoad()"];
2)通过JS延时操作setTimeout使得该方法在OC加载完毕后再执行
setTimeout("showMessage('参数在此')",1000)
三)交互方式
1.Block
1)JS调用OC
self.context[@"showMessage"] = ^(NSString *message){
NSLog(@"----showMessage-JS调用OC----参数为:%@",message); };
<button onclick="showMessage('我是参数')">showMessage-JS调用OC</button>
2)OC调用JS
//1.调用JS语句
[self.context evaluateScript:@"alert('你好!');"];
//2.调用JS中的变量
JSValue *myValue = self.context[@"myObject"];
NSLog(@"---%@---",[myValue toString]);
//3.调用JS中的方法(以下两种均可)
[self.context evaluateScript:@"OcCallJs()"];
[webView stringByEvaluatingJavaScriptFromString:@"OcCallJs()"];
//调用有参数的方法
JSValue *news = self.context[@"showNews"];
[news callWithArguments:@[@"新闻头条",@"时事政治"]];
<script>
var myObject = "我的项目哈哈哈";
function OcCallJs(){
alert("我被OC调用了");
}
function showNews(news,news2){
alert(news+news2);
}</script>
2.Delegate交互
当OC方法为多参数函数时,其在JS中调用需要更改一下以适应JS语法:
1)移除所有冒号
2)跟在冒号后面的参数的第一个字母大写
@protocol JSInteraction
-(void)showDelegateMessage:(NSString *)message;
-(void)showDelegateMessage:(NSString *)message andNumber:(NSInteger)number;
@end
-(void)showDelegateMessage:(NSString *)message{
NSLog(@"我是被JS调用的OC的delegate方法,参数为%@",message);
}
-(void)showDelegateMessage:(NSString *)message andNumber:(NSInteger)number{
NSLog(@"delegate多参数,参数为%ld--%@",number,message);
}
<script>
function webViewDidFinish(){
JsInteractive.showDelegateMessage('参数');
JsInteractive.showDelegateMessageAndNumber('123','1000000');
}
</script>
OcCallJs()方法在上文提到的webVIewDidFinishLoad()中调用
三、WKWebView
(一)定义
1.WKWebView
两种创建方式
//普通初始化
self.wkWebView = [[WKWebView alloc]init];
self.wkWebView.frame = self.view.bounds;
//config方式初始化
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
self.wkWebView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
self.wkWebView.navigationDelegate = self;
self.wkWebView.UIDelegate = self;
[self.view addSubview:self.wkWebView];
加载网页的方式
//加载本地URL文件
- (nullableWKNavigation*)loadFileURL:(NSURL*)URL allowingReadAccessToURL:(NSURL*)readAccessURL
//加载本地HTML字符串
- (nullableWKNavigation*)loadHTMLString:(NSString*)string baseURL:(nullableNSURL*)baseURL;
//加载二进制数据
- (nullableWKNavigation*)loadData:(NSData*)data MIMEType:(NSString*)MIMEType characterEncodingName:(NSString*)characterEncodingName baseURL:(NSURL*)baseURL
2.WKWebViewConfiguration
如上代码,webView初始化时配置webView的属性,JS调用原生时需要用到。
3.WKUserContentController
这个类主要用来负责原生与JS的交互管理
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
- (void)removeScriptMessageHandlerForName:(NSString *)name;
addScriptMessageHandler:name:方法注册要被JS调用的方法的名称,然后在对应的JavaScript中使用
window.webkit.messageHandlers.<name>.postMessage(<data>)
方法来向native发送消息。最后记得将方法名remove掉,否则会造成内存泄漏。
4.WKScriptMessageHandler
用来处理JS调用原生的方法
//接受JS发给原生的消息
- (void)userContentController:(WKUserContentController *)userContentControllerdidReceiveScriptMessage:(WKScriptMessage *)message;
5.WKUIDelegate
协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框)。提供用原生控件显示网页的方法回调。
(二)建立连接
1.文件头部引入相关库
#import <WebKit/WebKit.h>
2.加载HTML页面
NSURL *urlString = [[NSBundle mainBundle] URLForResource:@"JSInteraction.html" withExtension:nil];
if (urlString) {
[_wkWebView loadRequest:[NSURLRequest requestWithURL:urlString]];
}
WKUserContentController *userCC = config.userContentController;
(三)交互方式
1.原生调用JS
WKWebView 提供了一个新的方法evaluateJavaScript:completionHandler:,实现OC 调用JS 等场景。
//获取到input标签的value属性值
NSString *inputValue = @"document.getElementsByName('input')[0].attributes['value'].value";
/** native 调用JS的方法 */
[webView evaluateJavaScript:inputValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
NSLog(@"value:%@,error:%@",response,error);
}];
2.JS调用原生
/** JS调用OC **/
//此处相当于监听了JS中showMobile这个方法
[self.userCC addScriptMessageHandler:self name:@"showMobile"];
[self.userCC addScriptMessageHandler:self name:@"showName"];//移除WKUserContentController,防止内存泄漏
-(void)removeAllScriptHandler{
[self.userCC removeScriptMessageHandlerForName:@"showMobile"];
[self.userCC removeScriptMessageHandlerForName:@"showName"];
[self.userCC removeScriptMessageHandlerForName:@"showTwo"];
}
JS中:
//多参数用数组传递
function btnClick1() { window.webkit.messageHandlers.showMobile.postMessage(null) }
function btnClick2() {
window.webkit.messageHandlers.showName.postMessage('有个参数')
}
function btnClick3() {
window.webkit.messageHandlers.showTwo.postMessage(['有两个参数啦','canshu'])
}
三、拦截URL
(一)定义
我们要拦截URL,就要通过navigationDelegate的一个代理方法来实现。如果在HTML中要使用alert等弹窗,就必须得实现UIDelegate的相应代理方法。
(二)建立连接
同WKWebView。
(三)交互方式
1.JS调用OC
2.OC 调用 JS
JS 调用OC 方法后,有的操作可能需要将结果返回给JS。这时候就是OC 调用JS 方法的场景。
WKWebView 提供了一个新的方法evaluateJavaScript:completionHandler:,实现OC 调用JS 等场景。
使用自定义loadURL的原因:
如果当前网页正使用window.location.href加载网页的同时,调用window.location.href去调用OC原生方法,会导致加载网页的操作被取消掉。同样的,如果连续使用window.location.href执行两次OC原生调用,也有可能导致第一次的操作被取消掉。
自定义asyncAlert的原因
因为有的JS调用是需要OC 返回结果到JS的。stringByEvaluatingJavaScriptFromString是一个同步方法,会等待js 方法执行完成,而弹出的alert 也会阻塞界面等待用户响应,所以他们可能会造成死锁。导致alert 卡死界面。如果回调的JS 是一个耗时的操作,那么建议将耗时的操作也放入setTimeout的function中。
3.同步和异步
因为 iOS SDK 没有天生支持 js 和 native
相互调用,大家的技术方案都是自己实现的一套调用机制,所以这里面有同步异步的问题。细心的同学就能发现,js 调用 native 是通过插入一个
iframe,这个 iframe 插入完了就完了,执行的结果需要 native 另外用
stringByEvaluatingJavaScriptFromString 方法通知 js,所以这是一个异步的调用。
而 stringByEvaluatingJavaScriptFromString 方法本身会直接返回一个 NSString 类型的执行结果,所以这显然是一个同步调用。
所以 js call native 是异步,native call js 是同步。
四、WebViewJavascriptBridge
1.实质:拦截URL来实现的调用原生功能
2.JS中实现的方法
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
这个方法的作用主要是在第一次加载HTML的时候起作用,目的是加载一次wvjbscheme://__BRIDGE_LOADED__,来触发往HTML中注入一些已经写好的JS方法。
setupWebViewJavascriptBridge(function(bridge) {
/* Initialize your app here */
bridge.registerHandler('JS Echo', function(data, responseCallback) {
console.log("JS Echo called with:", data)
responseCallback(data)
})
bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
console.log("JS received response:", responseData)
})
})
Native 需要调用的 JS 功能,也是需要先注册,然后再执行的。如果Native 需要调用的JS 功能有多个,那么这些功能都要在这里先注册,注册之后才能够被Native 调用。
参照:
https://www.jianshu.com/p/7151987f012d
http://blog.devtang.com/2012/03/24/talk-about-uiwebview-and-phonegap/
OC与JS交互的更多相关文章
- OC和JS交互的三种方法
看简书上说一共有六种OC和JS交互的方法,但是前三种原理都一致,都是通过检测.拦截Url地址实现互相调用的.剩下的react native等第三方框架原理不一样,也没有去研究,下边记录我使用的三种方法 ...
- Mac Webview OC与JS交互实现
1.首先,需要定义一个JS可识别的变量(如external)用于OC与JS交互 - (void)webView:(WebView *)sender didClearWindowObject:(WebS ...
- iOS(UIWebView 和WKWebView)OC与JS交互 之二
在iOS应用的开发过程中,我们经常会使用到WebView,当我们对WebView进行操作的时候,有时会需要进行源生的操作.那么我记下来就与大家分享一下OC与JS交互. 首先先说第一种方法,并没有牵扯O ...
- OC与JS交互之WebViewJavascriptBridge
上一篇文章介绍了通过UIWebView实现了OC与JS交互的可能性及实现的原理,并且简单的实现了一个小的示例DEMO,当然也有一部分遗留问题,使用原生实现过程比较繁琐,代码难以维护.这篇文章主要介绍下 ...
- OC与JS交互之UIWebView
随着H5的强大,hybrid app已经成为当前互联网的大方向,单纯的native app和web app在某些方面显得就很劣势.关于H5的发展史,这里有一篇文章推荐给大家,今天我们来学习最基础的基于 ...
- OC与JS交互前言
OC与JS交互过程中,可能会需要使用本地image资源及html,css,js文件,这些资源应该如何被加载? 一.WebView加载HTML UIWebView提供了三个方法来加载html资源 1. ...
- WebViewJavascriptBridge源码探究--看OC和JS交互过程
今天把实现OC代码和JS代码交互的第三方库WebViewJavascriptBridge源码看了下,oc调用js方法我们是知道的,系统提供了stringByEvaluatingJavaScriptFr ...
- iOS(WKWebView)OC与JS交互 之三
随着H5功能愈发的强大,没进行过混合开发的小伙们都不好意思说自己能够独立进行iOS的app开发,在iOS7操作系统下,常用的native,js交互框架有easy-js,WebViewJavascr ...
- OC与JS交互之WKWebView
上一篇文章我们使用了JavaScriptCore框架重写了之前的示例,iOS8苹果偏爱HTML5,重构了UIWebVIew,给我们带来了WKWebView,使其性能.稳定性.功能大幅度提升,也更好的支 ...
- OC与JS交互之JavaScriptCore
JavaScriptCore提供了JavaScript和Objective-C桥接的Obj-C API.JavaScriptCore提供了让我们脱离UIWebView执行JavaScript脚本的能力 ...
随机推荐
- Django中间件,CSRF(跨站请求伪造),缓存,信号,BootStrap(模板)-响应式(栅格)+模板
Django中间件,CSRF(跨站请求伪造),缓存,信号,BootStrap(模板)-响应式(栅格)+模板 1.中间件(重要): 在Django的setting中有个MIDDLEWARE列表,里面的东 ...
- [NOIP2017(TG/PJ)] 真题选做
[NOIPTG2017] 小凯的疑惑 题意 小凯有两种面值的金币,每种金币有无数个,求在无法准确支付的物品中,最贵的价值是多少金币. 分析 设两种金币面值分别为 $a$ 和 $b \; (a<b ...
- ajax post
前端 $.post("url",JSON.stringify({ a:a, ... }), function(data){ }, 'json'); 后端 @PostMapping( ...
- apache 二级域名设置
1. 你要拥有一个有泛域名解析的顶级域名,例如:test.com 在dns服务上设置,域名服务商都提供此服务 www.test.com 指向服务器IPtest.com 指向 ...
- jenkins新建任务详解
一.新建任务 点击左侧新建任务 输入任务名称-->选择构建一个自由风格的软件项目-->确认 进入任务配置页面 二.基本配置 General 配置构建保留天数,保留个数,必要时并发构建 源码 ...
- 爬虫入门 beautifulsoup库(一)
先贴一个beautifulsoup的官方文档,https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#id12 requ ...
- python开发基础04-函数、递归、匿名函数、高阶函数、装饰器
匿名函数 lamba lambda x,y,z=1:x+y+z 匿名就是没有名字 def func(x,y,z=1): return x+y+z 匿名 lambda x,y,z=1:x+y+z #与函 ...
- opencv:轮廓逼近与拟合
轮廓逼近,本质上是减少编码点 拟合圆,生成最相似的圆或椭圆 #include <opencv2/opencv.hpp> #include <iostream> using na ...
- 一段代码实现Aplayer+网易云音乐接口
玩cnblogs的小伙伴可以将以下代码复制到页脚html当中哦 <!-- require APlayer --> <link rel="stylesheet" h ...
- 【Python】文件下载小助手
import requests from contextlib import closing class ProgressBar(object): def __init__(self, title, ...