一、WKWebView

  WKWebView 初始化时,有一个参数叫configuration,它是WKWebViewConfiguration类型的参数,而WKWebViewConfiguration有一个属性叫userContentController,它又是WKUserContentController类型的参数。

   WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.preferences = [[WKPreferences alloc] init];
config.preferences.minimumFontSize = ;
config.preferences.javaScriptEnabled = YES;
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
config.userContentController = [[WKUserContentController alloc] init];
config.processPool = [[WKProcessPool alloc] init];
config.userContentController = [WKUserContentController new];
//在创建wkWebView时,需要将被js调用的方法注册进去,oc与js端对应实现
[config.userContentController addScriptMessageHandler:self name:@"callFunciton"]; WKWebView *wkWebView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:config];
self.wkWebView = wkWebView;
wkWebView.navigationDelegate = self;
wkWebView.UIDelegate = self;
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:self.url];
[wkWebView loadRequest:request];
[self.view addSubview:wkWebView];

1.JS调用原生MessageHandler

WKUserContentController对象有一个方法

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

JS调用OC时,这句代码非常重要

// 在创建wkWebView时,需要将被js调用的方法注册进去,oc与js端对应实现
[config.userContentController addScriptMessageHandler:self name:@"callFunciton"];

  addScriptMessageHandler:name:有两个参数,第一个参数是userContentController的代理对象,第二个参数是JS里发送postMessage的对象。 
所以要使用MessageHandler功能,就必须要实现WKScriptMessageHandler协议。

1.实现WKScriptMessageHandler代理方法

  当js调用callFunction方法时,会回调此代理方法:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
if ([message.name isEqualToString:@"callFunction"]) {
//调用原生扫码
}
}

Tip:addScriptMessageHandler很容易引起循环引用,导致控制器无法被释放

- (void)dealloc{
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"callFunction"];
}

2.JS中使用方法:

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
//其中<name>,就是上面方法里的第二个参数`name`。
//例如我们调用API的时候第二个参数填@"callFunction",那么在JS里就是:
window.webkit.messageHandlers.callFunction.postMessage(<messageBody>)
//<messageBody>是一个键值对,键是body,值可以有多种类型的参数,body 的类型:Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull
messageBody可以为NULL或者其他参数,不能什么都不写,否则不走代理方法

2.原生调用JS

 [self.webView evaluateJavaScript:@"show()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {                
  //TODO
}];

3.WKNavigationDelegate

  可以在此通过连接的方式传递一些简单的参数,也是一种H5与原生交互

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {    
  NSString *url = navigationAction.request.URL.absoluteString; if(![url isEqualToString:self.strURL]) {
    // 页面跳转
}
decisionHandler(WKNavigationActionPolicyAllow);
}

二、WebViewJavaScriptBridge

WebViewJavaScriptBridge 用于 WKWebView & UIWebView 中 OC 和 JS 交互。
它的基本原理是: 把 OC 的方法注册到桥梁中,让 JS 去调用;把 JS 的方法注册在桥梁中,让 OC 去调用。

1. 初始化

1.导入头文件 #import <WebViewJavascriptBridge.h>

2.建立 WebViewJavaScriptBridge 和 WebView 之间的关系

_jsBridge = [WebViewJavascriptBridge bridgeForWebView:_webView];

3.在HTML 文件中,复制粘贴这两段 JS 函数

function setupWebViewJavascriptBridge(callback) {
  if (window.WebViewJavascriptBridge) {
    return callback(WebViewJavascriptBridge);
  }
  if (window.WVJBCallbacks) {
    return window.WVJBCallbacks.push(callback);
  }
  window.WVJBCallbacks = [callback]; // 创建一个 WVJBCallbacks 全局属性数组,并将 callback 插入到数组中。
  var WVJBIframe = document.createElement('iframe'); // 创建一个 iframe 元素
  WVJBIframe.style.display = 'none'; // 不显示
  WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'; // 设置 iframe 的 src 属性
  document.documentElement.appendChild(WVJBIframe); // 把 iframe 添加到当前文导航上。
  setTimeout(function() {
    document.documentElement.removeChild(WVJBIframe)
  }, )
} // 这里主要是注册OC将要调用的JS方法。
setupWebViewJavascriptBridge(function(bridge){ });

2. 注入OC、JS方法

往桥梁中注入 OC 方法

/* scanClick 是 OC block 的一个别名
* block本身,是JS通过某种方式调用到scanClick的时候,执行的代码块
* data,由于OC这端由JS调用,所以data是JS端传递过来的数据
* responseCallback OC端的block 执行完毕之后,往JS端传递的数据
*/
[_jsBridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
  NSLog(@"dataFrom JS : %@",data[@"data"]);
  responseCallback(@"扫描结果 : www.baidu.com");
}];

往桥梁中注入 JS 函数

/*
* estJavaScriptFunction: 是注入到桥梁中JS函数的别名,以供OC端调用。
* data: 回调函数的data,既然JS函数由OC调用,所以data是OC端传递过来的数据。
* responseCallback: JS调用在被OC调用完毕之后,向OC端传递的数据
*/
// 这里主要是注册 OC 将要调用的 JS 方法。
setupWebViewJavascriptBridge(function(bridge){
// 声明 OC 需要调用的 JS 方法。
bridge.registerHanlder('testJavaScriptFunction',function(data,responseCallback){
// data 是 OC 传递过来的数据.
// responseCallback 是 JS 调用完毕之后传递给 OC 的数据
alert("JS 被 OC 调用了.");
responseCallback({data: "js 的数据",from : "JS"});
})
});

3. 调用OC、JS方法

OC调用JS

// 单纯的调用 JSFunction,不往 JS 传递参数,也不需要 JSFunction 的返回值。
[_jsBridge callHandler:@"changeBGColor"];
// 调用 JSFunction,并向 JS 传递参数,但不需要 JSFunciton 的返回值。
[_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景颜色改成橙色!!!!"];
// 调用 JSFunction ,并向 JS 传递参数,也需要 JSFunction 的返回值。
[_jsBridge callHandler:@"changeBGColor" data:@"传递给 JS 的参数" responseCallback:^(id responseData) {
  NSLog(@"JS 的返回值: %@",responseData);
}];

JS调用OC

// JS单纯的调用OC的block
WebViewJavascriptBridge.callHandler('scanClick');
// JS调用OC的block,并传递JS参数
WebViewJavascriptBridge.callHandler('scanClick',"JS 参数");
// JS调用OC的block,传递JS参数,并接受OC的返回值。
WebViewJavascriptBridge.callHandler('scanClick',{data : "这是JS传递到OC的扫描数据"},function(dataFromOC){
  alert("JS 调用了 OC 的扫描方法!");
  document.getElementById("returnValue").value = dataFromOC;
});

4. OC释放Block

OC中,在当前控制器消失的时候,要记得把注入到桥梁中的 OC block,从桥梁中删除,否则,可能会出现控制器无法释放的情况。

[_jsBridge removeHandler:@"scanClick"];

5.示例

1.JS -> OC 的交互

在 OC 中,通过 WebViewJavascriptBridge 注册一个修改 navigationBar 颜色的 Block

[_jsBridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {
  self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:arc4random_uniform() / 255.0 green:arc4random_uniform() / 255.0 blue:arc4random_uniform() / 255.0 alpha:1.0];
  responseCallback(@"颜色修改完毕!");
}];

在 JS 中,通过某种方式去调用这个 OC 的 block。

WebViewJavascriptBridge.callHandler('colorClick',function(dataFromOC) {
  alert("JS 调用了 OC 注册的 colorClick 方法");
  document.getElementById("returnValue").value = dataFromOC;
})

OC -> JS 的交互

往桥梁中,注入一个修改 HTML body 颜色的 JSFunction。

// 在这里声明OC需要主动调用JS的方法。
setupWebViewJavascriptBridge(function(bridge) {
  bridge.registerHandler('changeBGColor',function(data,responseCallback){
    // alert('aaaaaa');
    document.body.style.backgroundColor = "orange";
    document.getElementById("returnValue").value = data;
});
});

然后在 OC 端通过桥梁调用这个 changeBGColor

 [_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景颜色改成橙色!!!!"];

iOS原生与H5交互的更多相关文章

  1. 客户端相关知识学习(三)之Android原生与H5交互的实现

    Android原生与H5交互的实现 H5调用原生的方式 方式可能有多种,根据开发经验,接触过两种方式. 方法一:Android向H5注入全局js对象,也就是H5调Android 1.首先对WebVie ...

  2. iOS JavaScriptCore与H5交互时出现异常提示

    在利用JavaScriptCore与H5交互时出现异常提示: This application is modifying the autolayout engine from a background ...

  3. iOS下原生与JS交互(总结)

    iOS开发免不了要与UIWebView打交道,然后就要涉及到JS与原生OC交互,今天总结一下JS与原生OC交互的两种方式. JS调用原生OC篇(我自己用的方式二,简单方便) 方式一 第一种方式是用JS ...

  4. 原生与JS交互 iOS

      前言 Hybrid App(混合模式移动应用)是指介于web-app.native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势” ...

  5. ios和安卓H5交互桥接

    ios交互 demo1(摘自网络) <!doctype html> <html> <head> <meta charset="UTF-8" ...

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

    现在的iOS项目中嵌入了越来越多的Web界面,当然是为了方便,那么为了迎合这一趋势,作为iOS开发程序员,我们必须要了解怎么样用OC去和这些Web界面进行交互.这里介绍的是JavaScriptCore ...

  7. 3.Appium处理原生与H5的嵌套

    环境前置准备 手机与电脑USB连接,开启USB调试模式,通过adb devices可查看到此设备. 电脑端.移动端安装chrome浏览器.(尽量保证移动端chrome版本低于电脑端) App webv ...

  8. OVGap 原生与JS交互

    源代码:https://github.com/windshg/OVGap OVGap:一个轻量级的类库,能够让iOS应用和远程网页的 Javascript 代码进行通信,也就是说,远程的 Javasc ...

  9. 安卓原生与hml交互(WebView基础)

    WebView加载页面 webView有两种加载方式, 加载网络地址 webView.loadUrl("www.xxx.com/index.html"); 加载本地资源 webVi ...

随机推荐

  1. AdaptIS: Adaptive Instance Selection Network

    AdaptIS: Adaptive Instance Selection Network 2019-09-19 12:58:07 Paper: https://arxiv.org/pdf/1909.0 ...

  2. Nginx配置SSL证书部署HTTPS网站(颁发证书)

    一.Http与Https的区别HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高 ...

  3. Python中的虚拟环境的使用

    1.安装virtualenv pip3 install virtualenv 2.创建目录 mkdir Myproject cd Myproject 3.创建独立运行环境-命名 virtualenv ...

  4. [转]c++ 开发 sqlite

    #include <iostream> #include <sqlite3.h> using namespace std; int main() { cout << ...

  5. vue的mixin简化开发

    vue的mixin可以将多个组件公用的声明周期方法和数据封装成一个对象,在不同的组件中自由插拔.实际做项目的时候,可以定义一些mixin,供多个组件使用.也非常有必要定义一个全局的mixin对象,对所 ...

  6. LODOP纸张/打印机/份数/打印方向/双面打印 简短问答

    纸张#如何设置纸张纸张设置,参考样例5 http://www.c-lodop.com/demolist/PrintSample5.html纸张的一些优先级 http://www.c-lodop.com ...

  7. 消息发送函数OSMboxPostOpt()

    消息发送函数OSMboxPostOpt() 作用,ucos 3中的消息邮箱,具有广播功能,发送一条消息就可以使所有等待该消息的任务进入就绪状态,从而完成消息分发功能,具有一个消息唤醒多个任务的机制.

  8. C#基于RabbitMQ实现客户端之间消息通讯实战演练

    一.背景介绍和描述 MQ消息队列已经逐渐成为企业IT系统内部通信的核心手段.它具有低耦合.可靠投递.广播.流量控制.最终一致性等一系列功能,成为异步RPC的主要手段之一.何时需要消息队列?当你需要使用 ...

  9. k8s 修改节点角色和删除节点

    修改节点角色: kubectl label nodes cn-thin05 node-role.kubernetes.io/node= 卸载节点: kubectl drain jupiter --de ...

  10. tp5之服务器不显示验证码

    今天在使用tp框架的时候遇到的一个bug,前辈们早已有解决方法,遇到了做个笔记 TP5框架,自带的验证码在本地localhost运行是没问题的,可以正常显示,如图: 然后,把框架拿到服务器是去运行,验 ...