美文网首页wkwebview我爱编程iOS学习
OC与JS交互—— UIWebView,WKWebView

OC与JS交互—— UIWebView,WKWebView

作者: 墨痕未干 | 来源:发表于2017-03-24 10:33 被阅读631次

    iOS中用来加载网页,有两个控件UIWebView(iOS8之前),WKWebView(iOS8诞生)。WKWebView比UIWebView有太多的优势了,加载速度快,交互便捷等等。iOS8之后肯定要适配WKWebView。如果要适配到iOS7,项目里就必须要用这两个控件了。下面具体说一下这两个控件与JS的交互。

    UIWebView交互Demo:http://onarkxpx4.bkt.clouddn.com/WebViewJavascriptBridgeDemo-master.zip

    WKWebView交互Demo:http://onarkxpx4.bkt.clouddn.com/WKWebView与JS交互.zip

    UIWebView交互

    • 代理方法交互 (自带方法)

    拦截链接,从而获取链接里的信息,只能进行JS专递信息到OC。非常简陋,只适用超轻量级交互。

    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
        
        //    NSLog(@"Scheme: %@", [url scheme]);  //方案
        //    NSLog(@"Host: %@", [url host]);    //主机
        //    NSLog(@"Port: %@", [url port]);    //端口
        //    NSLog(@"Path: %@", [url path]);    //路径
        //    NSLog(@"Relative path: %@", [url relativePath]); //相关路径
        //    NSLog(@"Path components as array: %@", [url pathComponents]);  //路径的每个组成部分
        //    NSLog(@"Parameter string: %@", [url parameterString]);   //参数
        //    NSLog(@"Query: %@", [url query]);                        //查询
        //    NSLog(@"Fragment: %@", [url fragment]);                  //分段
    
        //当前的链接
        NSURL* url = [request URL];
        NSString * string = [url absoluteString];
        //链接转为字典,获取我们想要的信息
        NSDictionary* dic = [self dictionaryFromQuery:string usingEncoding:NSUTF8StringEncoding];
        NSLog(@"%@",dic);
        //return NO 不允许跳转链接
        return YES;
        
    }
    

    链接转字典的工具方法

    - (NSDictionary*)dictionaryFromQuery:(NSString*)query usingEncoding:(NSStringEncoding)encoding {
        NSCharacterSet* delimiterSet = [NSCharacterSet characterSetWithCharactersInString:@"&;"];
        NSMutableDictionary* pairs = [NSMutableDictionary dictionary];
        NSScanner* scanner = [[NSScanner alloc] initWithString:query];
        while (![scanner isAtEnd]) {
            NSString* pairString = nil;
            [scanner scanUpToCharactersFromSet:delimiterSet intoString:&pairString];
            [scanner scanCharactersFromSet:delimiterSet intoString:NULL];
            NSArray* kvPair = [pairString componentsSeparatedByString:@"="];
            if (kvPair.count == 2) {
                NSString* key = [[kvPair objectAtIndex:0]
                                 stringByReplacingPercentEscapesUsingEncoding:encoding];
                NSString* value = [[kvPair objectAtIndex:1]
                                   stringByReplacingPercentEscapesUsingEncoding:encoding];
                [pairs setObject:value forKey:key];
            }
        }
        
        return [NSDictionary dictionaryWithDictionary:pairs];
    }
    

    UIWebView给的原生方法里对OC与JS交互太乏力了。所以作为github上8千多star的第三方框架,WebViewJavascriptBridge作为UIWebView交互工具无疑是极为优秀的,使用也极为简单。

    OC代码

    JS调用OC方法(JS给OC传数据)

    //基本设置
    [WebViewJavascriptBridge enableLogging];//开启日志
    WebViewJavascriptBridge* bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
    [bridge setWebViewDelegate:self];
    //方法注册,AppClosePrice为方法名
    [bridge registerHandler:@"AppClosePrice" handler:^(id data, WVJBResponseCallback responseCallback) {
            //data为JS传来的数据
            NSLog(@"%@", data);
            [self.navigationController popViewControllerAnimated:YES];
            
        }];
    

    ** OC调用JS方法(OC给JS传数据,也可以接收JS传过来的数据)**
    基本设置同上,就是注册方法的时候不同。

    //AppHidden:方法名,appCurVersion:OC给JS传的数据
    [self.bridge callHandler:@"AppHidden" data:appCurVersion responseCallback:^(id responseData) {
    
            NSLog(@"----JS传过来的数据----%@",responseData);
        }];
    

    JS代码(由后台实现,可以给后台参考)

     /*这段代码是固定的,必须要放到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 = 'wvjbscheme://__BRIDGE_LOADED__';
            document.documentElement.appendChild(WVJBIframe);
            setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
          }
        
          /*与OC交互的所有JS方法都要放在此处注册,才能调用通过JS调用OC或者让OC调用这里的JS*/
          setupWebViewJavascriptBridge(function(bridge) {
           var uniqueId = 1
                                       
           function log(message, data) {
             var log = document.getElementById('log')
             var el = document.createElement('div')
             el.className = 'logLine'
             el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
             if (log.children.length) {
                log.insertBefore(el, log.children[0])
             } else {
               log.appendChild(el)
             }
           }
           /* Initialize your app here */
           
           /*注册一个JS调用OC的方法,不带参数*/
           bridge.registerHandler('AppClosePrice', function() {
              log("JS调用OC")
           })
            /*注册一个JS调用OC的方法,带参数*/
           bridge.registerHandler('AppClosePrice', function(data, responseCallback) {
                                  
             log("Get user information from OC: ", data)
           })
           /*注册一个OC调用JS的方法*/
           bridge.callHandler('AppHidden', function(responseData) {
             log("JS call ObjC's getUserIdFromObjC function, and js received response:", responseData)
           })
    

    WKWebView交互

    WKWebView作为UIWebView的替代品,具有完备的JS交互方法,我们可以不借助第三方,就进行OC与JS的深度交互。并且JS端的代码也极其简易。

    JS调用OC方法(JS给OC传数据)

    交互的方法名定义好,OC,JS都需要进行注册。

    OC端注册方法(IOSClosePrice)

        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        config.userContentController = [[WKUserContentController alloc] init];
        [config.userContentController addScriptMessageHandler:self name:@"IOSClosePrice"];
        WKWebView *webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - 14) configuration:config];
    

    JS端注册方法(IOSClosePrice)

    function payClick() {
            window.webkit.messageHandlers.IOSClosePrice.postMessage({order_no:'201511120981234',channel:'wx',amount:1,subject:'粉色外套'});
        }
    

    OC端接收数据

    WKWebView的协议WKScriptMessageHandler里面的方法,JS向OC传任何数据,OC都可以通过该方法接收,通过JS传过来的数据,我们可以调用相应OC的方法,从而达到JS调用OC方法的目的。

    /**
     * JS给原生传数据:JS调用原生的方法
     */
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
        
        NSDictionary *dict = message.body;
        NSString* action = [dict valueForKey:@"action"];
        NSLog(@"----WKwebView交互----%@",action);
        
        if ([action isEqualToString:@"action1"]) {
            [self action1];
        }
    
        if ([action isEqualToString:@"action2"]) {
            [self action2];
        }
    }
    

    OC调JS的方法(OC向JS传数据)

    OC端代码

    payResult 为规定好的方法名,JS端用这个方法名接收数据。

        // 将支付结果返回给js
        //这里的payResult()是JS的语言
        NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"];
        [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            NSLog(@"%@----%@",result, error);
        }];
    

    注意:当JS调用OC的时候也可以用这个方法给JS进行回传数据,和上面的WKScriptMessageHandler代理中的方法相结合,就是实现了OC和JS的数据交流

    JS端代码

    function payResult(str) {
                    asyncAlert(str);
                    document.getElementById("returnValue").value = str;
                }
    

    总结

    从上面分析可以看出,UIWebView作为古老的网页加载控件,只能靠三方才能进行方便的JS交互,虽然WebViewJavascriptBridge使用起来很方便,但是局限性还是有的。而WKWebView作为UIWebView的替代。在OC与JS交互上面体现了强大的能力。WKWebView更注重的加强了OC和JS之间的数据通讯。双方的代码量都极为简洁,数据交互也一目了然。

    相关文章

      网友评论

      • 洁简:怎么给js端传字典呢
        墨痕未干:@洁简 可以的,这样:
        //这里的payResult()是JS的语言
        NSDictionary* dic = [[NSDictionary alloc]init];
        NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",dic];
        [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@----%@",result, error);
        }];
        洁简:@墨痕未干 wkwebview 就不可以了
        墨痕未干:[self.bridge callHandler:@"AppHidden" data:appCurVersion responseCallback:^(id responseData) {

        NSLog(@"----JS传过来的数据----%@",responseData);
        }];

        这个方法中appCurVersion可以是字符串,也可以是字典。
      • 天地不仁以万物为刍狗:明天要用,赞一个
        墨痕未干:谢谢支持,有问题随时交流

      本文标题:OC与JS交互—— UIWebView,WKWebView

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