• 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];

    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)
      }, 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 函数

    /*
    *  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(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 的背景颜色改成橙色!!!!"];
  • 相关阅读:
    教材全解
    知乎、博客园等开放API接口
    学习正则表达式就这么简单
    C#操作域用户ADHelper
    跨线程时使用静态扩展方法更新控件
    C#中的WinForm的消息机制简述,及消息机制下Invoke,和BeginInvoke的使用和区别
    WinForm 捕获异常 Application.ThreadException + AppDomain.CurrentDomain.UnhandledException
    Winform异常处理之ThreadException、unhandledException及多线程异常处理
    深入理解C#中的IDisposable接口
    批处理应用的几个技巧
  • 原文地址:https://www.cnblogs.com/liuluoxing/p/11769596.html
Copyright © 2020-2023  润新知