美文网首页iOS 开发
iOS 开发之 OC 与 JS 的通信桥梁:WebViewJav

iOS 开发之 OC 与 JS 的通信桥梁:WebViewJav

作者: 且听风吟SW | 来源:发表于2019-11-22 14:50 被阅读0次

    前言

    当下,移动端 APP 嵌套 HTML5 网页已成常态,我们除了简单地用 WebView 加载显示之外,很多情况下,我们还需要和 WebView 进行交互,比如点击 WebView 上面的按钮调用 OC 端的函数实现具体的操作……

    iOS 开发中,H5 和原生交互有多种方式, JSBridge 就是最常用的一种,各 JSBridge 类库的实现原理大同小异…今天呢,介绍一下盛名已久的 WebViewJavascriptBridge ,它主要帮助我们优雅地实现 OC 与 JS 的交互,非常方便简洁。

    目录结构


    WebViewJavascriptBridge
    1. WebViewJavascriptBridge_JS

    JS 端用来收发消息的类,纯 JS 代码。其做为 html 的 bridge,负责(主动)对原生界面发送消息,整理并(被动的向原生界面)传输数据,并向 html 发送回调信息;

    1. WebViewJavascriptBridge

    桥接的入口,针对不同类型的 webView(UIWebView,WKWebView,WebView)进行分发;
    针对 UIWebView 和 WebView 做的一层封装,主要用来执行 JS 代码,以及实现 UIWebView 和 WebView的代理方法,并通过拦截 URL 来通知 WebViewJavascriptBridgeBase 做相应操作

    1. WKWebViewJavascriptBridge

    针对 WKWebView 做的一层封装,主要用来执行 JS 代码,以及实现 WKWebView 的代理方法,并通过拦截 URL 来通知 WebViewJavascriptBridgeBase 做相应操作

    1. WebViewJavascriptBridgeBase

    用来进行 bridge 初始化和消息处理的核心类;
    这个类是在支持 WKWebView 后从 WebViewJavascriptBridge 中独立出来的逻辑,专门用来处理 bridge 相关的逻辑,不再与具体的 Web View 相关联了

    理一下它们的关系

    WebViewJavascriptBridge_JS 可以看成是 html 文件中 JS 代码的 bridge,主要服务于 html 中的 JS 方法功能的;另外的三个 WebViewJavascriptBridge, WKWebViewJavascriptBridge, WebViewJavascriptBridgeBase 是 webView 的 bridge,主要服务于原生的。这两个 bridge 之间主要通过 webView 的代理方法webView:decidePolicyForNavigationAction:decisionHandler: 和 stringByEvaluatingJavaScriptFromString: 方法进行消息的传递和数据的传输。

    众所周知,WKWebView 和 UIWebView 的性能差异很大。WKWebView 比 UIWebView 加载网页的速度更快,效率更高,内存消耗小,所以现在在开发中我都是选择使用 WKWebView。

    并且,在当前时间节点,大多数 iOS App 都是基于 iOS 8.0+ 的,这是一个 WKWebView 的时代。

    使用步骤

    1、导入 WebViewJavaScriptBridge 框架:

    pod ‘WebViewJavascriptBridge’
    

    2、创建 WKWebView 和 WKWebViewJavascriptBridge 示例:

    #import <WebViewJavascriptBridge.h> 
    
    @interface H5WebViewController ()<WKNavigationDelegate, WKUIDelegate>
    @property (strong, nonatomic) WKWebView *webView;
    @property WKWebViewJavascriptBridge *bridge;
    
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self setUpWebView];
        [self setUpWebViewJavascriptBridge];
    }
    
    - (void)setUpWebView {
        self.wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds];
        self.wkWebView.navigationDelegate = self;
        self.wkWebView.UIDelegate = self;
        [self.view addSubview:self.wkWebView];
    }
    
    /**
     * 使用 WebViewJavascriptBridge 实现 OC 与 JS 交互
     */
    - (void)setUpWebViewJavascriptBridge {
        if (_bridge) {
            return;
        }
        
        // 设置能够进行桥接
        [WebViewJavascriptBridge enableLogging];
        
        // 初始化 WebViewJavascriptBridge 实例, 设置代理, 进行桥接
        _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
        [_bridge setWebViewDelegate:self];
        
        
        __weak __typeof(self)weakSelf = self;
    
        /**
         JS 调用 OC ,设置导航条 title
         
         @param data 后台 JS 页面传过来的参数
         @param registerHandler 要注册的事件名称(这里我们为 locationAlertViewWithMessage)
         @param handler 回调 block 函数 当后台触发这个事件的时候会执行 block 里面的代码
         */
        [_bridge registerHandler:@"_app_setTitle" handler:^(id data, WVJBResponseCallback responseCallback) {
            if (data) {
                NSDictionary *dic = (NSDictionary *)data;
                weakSelf.title = dic[@"title"];
            }
            // responseCallback 给后台 JS 的回复
            responseCallback(@"OK,已收到标题信息!");
        }];
        
        /**
         * JS 调用 OC ,关闭当前 H5 控制器
         */
        [_bridge registerHandler:@"_app_closeWebView" handler:^(id data, WVJBResponseCallback responseCallback) {
            [weakSelf closeTheView];
            responseCallback(@"OK,已关闭当前 WebView ");
        }];
        
    
        /**
         * OC 调用 JS ,获取 OC 的值
         */
        [_bridge callHandler:@"_app_getToken" data:@"userToken"];
    }
    
    - (void)dealloc {
        [_bridge removeHandler:@"_app_setTitle"];
        [_bridge removeHandler:@"_app_closeWebView"];
        [_bridge removeHandler:@"_app_getToken"]; 
    }
    
    // 关闭当前窗口
    - (void)closeTheView { 
        [self dismissViewControllerAnimated:YES completion:nil];
        [self.navigationController popViewControllerAnimated:YES];
    }
    

    3、HTML 代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
        <title>Document</title>
    </head>
    <body>
        <div id="JSCallOC">js调用oc</div>
        <div id="textOne"></div>
    </body>
    
    <script>
        // 固定写法 函数名字可变 这个方法是必须的
        <!-- 申明交互 -->
        function setupWebViewJavascriptBridge(callback) {
            if (window.WebViewJavascriptBridge) {
               return callback(WebViewJavascriptBridge); 
            } else {
               document.addEventListener('WebViewJavascriptBridgeReady' , function() {
                                                              callback(WebViewJavascriptBridge)
                                                              }, false );
            }
            if (window.WVJBCallbacks) { 
               return window.WVJBCallbacks.push(callback); 
            }
            window.WVJBCallbacks = [callback];
            var WVJBIframe = document.createElement('iframe');
            WVJBIframe.style.display = 'none';
            WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
            document.documentElement.appendChild(WVJBIframe);
            setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
        }
    
        //这也是固定的,OC调JS,需要给OC调用的函数必须写在这个函数里面
        setupWebViewJavascriptBridge(function(bridge) {
            //oc调js不带参数不带回调
            bridge.registerHandler('OCCallJS',function() {
                alert("oc调js不带参数不带回调");
            })
            //oc调用js带参数不带回调
            bridge.registerHandler('OCCallJSParameter',function(params) {
                alert("oc调js带参数不带回调"+params);
            })
            //oc调js带参数带回调
            bridge.registerHandler('OCCallJSParameterBlock',function (params,responseCallback) {
                alert("oc调js带参数带回调");
                responseCallback("参数回调");
            })
        })
        // js调用oc
        // 每个方法的特殊处理                      
        function setTitle() {                            
             WebViewJavascriptBridge.callHandler('_app_setTitle', '这是一个nav标题',
             function (response) {
                   // 移动端回传的数据
                   alert('移动端回传的数据:' + response);
              });
        }
    
    </script>
    </html>
    

    结束语

    WebViewJavascriptBridge 使用起来比较方便快捷,并且还有 Android 版本的。如果 Android 和 iOS 都使用的话,就避免了前端既要写一份 Android 又要写一份 iOS 的 JS 与 OC 交互,调试起来只要一端通了,JS 那边应该就没啥问题,排查问题方便、高效,不用针对两个端分别设置不同的代码,统一性很重要哦。

    如有不足,欢迎批评指正!

    相关文章

      网友评论

        本文标题:iOS 开发之 OC 与 JS 的通信桥梁:WebViewJav

        本文链接:https://www.haomeiwen.com/subject/jgdtectx.html