iOS原生与H5交互

一、WKWebView

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

   WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.preferences = [[WKPreferences alloc] init];
    config.preferences.minimumFontSize = 10;
    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];</pre>

1.JS调用原生MessageHandler

WKUserContentController对象有一个方法

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

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

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

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

1.1.实现WKScriptMessageHandler代理方法

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

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ if ([message.name isEqualToString:@"callFunction"]) { 
    NSString *methodName = message.name;
    id params = message.body;
    //调用原生扫码 
    if ([methodName isEqualToString:@"scan"]) {
       
    }
 } 
}

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

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

1.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

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

 //2有参数
 NSDictionary *dict = @{ @"userAgent": @“userAgentM”, @"custMac":@“custMac”};
 NSString *callBackString = [NSString stringWithFormat:@"show(%@)",[self jsonToString:dict]];
 [self.webView evaluateJavaScript:callBackString completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
        
 }];

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) 
  }, 0)
} 
// 这里主要是注册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 函数

/* testJavaScriptFunction: 是注入到桥梁中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(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 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交互
参考: WebViewJavaScriptBridge 基本使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容