zoukankan      html  css  js  c++  java
  • iOS WebViewJavascriptBridge初步尝试与图文详细讲解

    JS和OC的交互这是个永恒话题,使用场景也是越来越多,如今一些reactnative、vue框架等,都是在重点结合原生与H5的混合使用。

    那么,如何快捷方便的使用两者交互是一个很重要的关键点。

    1、传统上的交互使用:

    - OC调用JS:

    webView对象通过调用stringByEvaluatingJavaScriptFromString这个方法执行一段JS代码实现交互。

    如:

    OC代码:

    [self.webView stringByEvaluatingJavaScriptFromString:@"ocCallJS({'name':'xiaoxiao'})"];

    JS代码:

    </script>
       function ocCallJS(data) {
        var obj = eval(data);
        alert(obj.name);
       }
    <script>

    这种方式对一些简单场景比较适用,也很方便。

    - JS调用OC:

    webView拦截url链接,获取内容,再处理逻辑

    如:

    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

    实现以上webView的代理方法,当webView每次开始加载URL时会进入这个方法,我们便可以在这个方法实现JS调用OC。

    举例大概如下:

    JS代码:

    OC代码:

    (图片来源于网络)

    这种JS调用OC的方法的缺点十分明显,需要繁琐地解释字符串得到相应的方法名和传值,且调用的方法也不能传递返回值;

    但优点是:不需要等待页面加载完才触发,当相应的代码被运行就能调用OC的方法(相比 JavaScriptCore而言,下文会讲到)。

    2、苹果推荐的框架--JavaScriptCore

    JavaScriptCore是苹果在iOS7时新推出用以实现JS和iOS代码交互的框架,十分简单高效。

    使用这种,需要导入JavaScriptCore.framework框架。

    oc调用js时:

    -(void)webViewDidFinishLoad:(UIWebView *)webView  
    {  
        //网页加载完成调用此方法  
          
        //首先创建JSContext 对象(此处通过当前webView的键获取到jscontext)  
        JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];  
        NSString *alertJS=@"alert('test js OC')"; //准备执行的js代码  
        [context evaluateScript:alertJS];//通过oc方法调用js的alert  
          
    } 

    这句话的意思,是在webview加载结束,注入一段js代码,和传统方式有异曲同工之处。

    js调用oc时:

    这个是这个框架的优势重点。这里基本解决了两者的调用,并且能够实现,调用之后的回调处理。

    举例:使用block方式比较简单,也是我比价推荐的一种,但是要注意防止循环引用问题的发生。


    HTML文件按钮代码

     <button onclick="myAction(str);" style="">点击按钮返回上一个页面</button>

    OC中代码

    - (void)webViewDidFinishLoad:(UIWebView *)webView方法中对block块进行代码实现.
    
      __weak typeof(self)temp = self;
      self.context[@"myAction"] = ^(NSString *str){
          //如果有参数,就是str
          [temp.navigationController popViewControllerAnimated:YES];
    
      };

    但!!!这里有个坑,就是oc调用js,必须是html加载完成之后(webViewDidFinishLoad)才可以。

    3、WKScriptMessageHandler

    注意:使用时必须iOS8+以及WKWebView(其实WKWebView并不支持方法二),如果用wk,我们可以用WKWebView的WKScriptMessageHandler 来实现交互。

    第一步:初始化WKWebView,调用addScriptMessageHandler:name:方法,name为js中的方法名,如scan:

    - (void)setupWKWebView{
      WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
      configuration.userContentController = [[WKUserContentController alloc] init];
      [configuration.userContentController addScriptMessageHandler:self name:@"funcname"];
    
      WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
      webView.UIDelegate = self;
    }

    oc调用js:

    //OC调用JS
    NSString *JSResult = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
    
    [self.webView evaluateJavaScript:JSResult completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@", error);
    }];

    js调用oc:

    h5:

    func(){
        window.webkit.messageHandlers.funcname.postMessage() 
    }

      // 注意:这里funcname是方法名,必须和oc里定义的一致

     

    oc:在WKScriptMessageHandler代理方法,当js调用scan方法时,会回调此代理方法:

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

    4、优秀的第三方框架--WebViewJavascriptBridge

    到了这里,就是本文的重点了,介绍一下WebViewJavascriptBridge

    GitHub地址

    WebViewJavascriptBridge同时支持UIWeView和WKWebView,无论是JS调用OC还是OC调用JS,都可以正常传值和返回值;而且在页面加载时只要JS代码被运行就可以进行交互,上面遇到的缺点和坑在这里都被掩埋的,所以是现在处理交互的主流做法。

    网上我找的资料,很多也是从官方demo衍生出来的,本文也不例外,不同的是,我会在代码里,详细的添加各种注释,保证大家能快速的理解。

     

    使用介绍:

    1、准备文件

    2、js代码截取片段

    解释:这段代码是必须的,申明交互直接拷贝即可,处理交互部分,需要改动,关键就是和oc端协商的方法名,以及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)
        }
    
        <!-- 处理交互  方法名要和ios内定义的对应-->
        setupWebViewJavascriptBridge(function(bridge) {
    
            <!--处理 oc 调用 js -->
            bridge.registerHandler('registerAction', function(data, responseCallback) {
                //处理oc给的传参
                alert('oc请求js  传值参数是:'+data)                               
                var responseData = { 'result':'handle success' }
                //处理完,回调传值给oc
                responseCallback(responseData)
            })
    
            var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
            callbackButton.innerHTML = '点击我,我会调用oc的方法'
            callbackButton.onclick = function(e) {
                e.preventDefault()                                 
                <!--处理 js 调用 oc -->
                bridge.callHandler('loginAction', {'userId':'zhangsan','name': '章三'}, function(response) {
                     //处理oc过来的回调
                     alert('收到oc过来的回调:'+response)
                })
            }
        })

    3、OC代码

    - pod导入框架

    pod 'WebViewJavascriptBridge'

    - import头部

    #import "WebViewJavascriptBridge.h"

    - viewDidload

        //初始化  WebViewJavascriptBridge
        if (_bridge) { return; }
        [WebViewJavascriptBridge enableLogging];    
        _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
        [_bridge setWebViewDelegate:self];    
        
        //请求加载html,注意:这里h5加载完,会自动执行一个调用oc的方法
        [self loadExamplePage:webView];    
        
        //申明js调用oc方法的处理事件,这里写了后,h5那边只要请求了,oc内部就会响应
        [self JS2OC];    
      
        //模拟操作:2秒后,oc会调用js的方法
        //注意:这里厉害的是,我们不需要等待html加载完成,就能处理oc的请求事件;此外,webview的request 也可以在这个请求后面执行(可以把上面的[self loadExamplePage:webView]放到[self OC2JS]后面执行,结果是一样的)
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self OC2JS];        
        });

     - JS  调用  OC

    -(void)JS2OC{
        /*
         含义:JS调用OC
         @param registerHandler 要注册的事件名称(比如这里我们为loginAction)
         @param handel 回调block函数 当后台触发这个事件的时候会执行block里面的代码
         */
        [_bridge registerHandler:@"loginAction" handler:^(id data, WVJBResponseCallback responseCallback) {
            // data js页面传过来的参数  假设这里是用户名和姓名,字典格式
            NSLog(@"JS调用OC,并传值过来");
            
            // 利用data参数处理自己的逻辑
            NSDictionary *dict = (NSDictionary *)data;
            NSString *str = [NSString stringWithFormat:@"用户名:%@  姓名:%@",dict[@"userId"],dict[@"name"]];
            [self renderButtons:str];
            
            // responseCallback 给js的回复
            responseCallback(@"报告,oc已收到js的请求");
        }];
    
    }

    -  OC  调用  JS

    -(void)OC2JS{
        /*
         含义:OC调用JS
         @param callHandler 商定的事件名称,用来调用网页里面相应的事件实现
         @param data id类型,相当于我们函数中的参数,向网页传递函数执行需要的参数
         注意,这里callHandler分3种,根据需不需要传参数和需不需要后台返回执行结果来决定用哪个
         */
        
        //[_bridge callHandler:@"registerAction" data:@"我是oc请求js的参数"];
        [_bridge callHandler:@"registerAction" data:@"uid:123 pwd:123" responseCallback:^(id responseData) {
            NSLog(@"oc请求js后接受的回调结果:%@",responseData);
        }];
        
    }

    这里的关键点就是:OC和JS商定的方法名要统一,两端要合作一下。

    这里的举例,我都用到了处理后的回调,大家运行demo的时候注意看日志文件。

    源码下载:点击这里获取Demo

    以上几种方法就到这里里,如果需求比较简单,不一定需要使用最后一种,灵活运用更方便~  

    enjoy

  • 相关阅读:
    Codeforces Round #394 (Div. 2) A. Dasha and Stairs
    HDU 1410 PK武林盟主
    HDU 3362 Fix(状压dp)
    P678-vect2.cpp
    Qt5获取本机网络信息
    Qt5标准文件对话框类
    nRF52832无法加载协议栈文件
    Debug记录(1)
    大小端模式
    nRF52832的SAADC
  • 原文地址:https://www.cnblogs.com/yajunLi/p/6369257.html
Copyright © 2011-2022 走看看