美文网首页
使用WebViewJavascriptBridge实现与UIWe

使用WebViewJavascriptBridge实现与UIWe

作者: 考v考 | 来源:发表于2016-07-15 14:34 被阅读0次

    最近在做的项目遇到这么一个需求:网络加载一段HTML格式的只有body部分的文本,自己本地拼写完整的HTML,图片需带点击放大浏览功能(移动端实现)
    分析需求之后发现难点在如何与UIWebview进行交互,在查阅资料后,找到http://kittenyang.com/webview-javascript-bridge
    KittenYang大神完美解决了我的问题,但过程中还是出了不少问题,这里就写下按照大神的思路写的具体实现过程
    这里主要用了WebViewJavascriptBridge,SDWebImage,YYPhotoGroup这三个第三方库。

    WebViewJavascriptBridge使用简介

    HTML文本的<script></script>标签中间添加如下代码

    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)}
    
    setupWebViewJavascriptBridge(function(bridge) {
        //方法名,传递的参数,回调
        bridge.callHandler('testObjcCallback', 'Hello world', function(response)){
        };
        //方法名,收到的参数,回调方法
        bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
         var message = data['foo']
         console.log(message)
    })
    

    在OC端需实现
    @property(strong,nonatomic) WebViewJavascriptBridge* bridge;

     _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
    //注册方法
    [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"testObjcCallback called: %@", data);
        responseCallback(@"Response from testObjcCallback");
    }];
    //调用JS的方法并传参
    [_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }];
    

    完成上面的步骤后就可以实现与UIWebView的JS交互,但是要实现需求中所提到的功能还需要更深层次的交互,还需要一定的前端知识。

    流程设计

    流程图

    功能实现

    1. HTML文本处理

       //获取本地HTML路径
       NSString* HTMLPath = [[NSBundle mainBundle] pathForResource:@"HTMLDemo" ofType:@"html"];
       //获取HTML字符串
       NSMutableString* HTMLString = [NSMutableString stringWithContentsOfFile:HTMLPath encoding:NSUTF8StringEncoding error:nil];
       //设定需要替换的字符串
       NSRange range = [HTMLString rangeOfString:@"<P>mainviews</P>"];
       //将字符串中的src标签都替换掉,防止加载HTML的时候预加载图片
       NSString *replace = [self.HTMLBoday stringByReplacingOccurrencesOfString:@"src=" withString:@"esrc="];
       //将字符串替换调
       [HTMLString replaceOccurrencesOfString:@"<P>mainviews</P>" withString:replace options:NSCaseInsensitiveSearch range:range];
       //根据正则表达式查找img标签
       NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(<img[ ^>]+esrc=\")(\\S+)\"" options:0 error:nil];
       //给img标签添加点击事件
       NSString *finalHTMLString = [regex stringByReplacingMatchesInString:HTMLString options:0 range:NSMakeRange(0, HTMLString.length) withTemplate:@"<img esrc=\"$2\" onClick=\"javascript:onImageClick('$2')\" "];
       //加载HTML
       [self.webView loadHTMLString:finalHTMLString baseURL:[NSURL fileURLWithPath:HTMLPath]];
      
    2. 获取所有img的src
      JS端

       bridge.registerHandler('getImageUrlsArray', function(data, responseCallback) {
       //获取所有img标签
       var allImage = document.querySelectorAll("img");
       //Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组
       allImage = Array.prototype.slice.call(allImage, 0);
       var imageUrlsArray = new Array();
       allImage.forEach(function(image) {
           //将所有图片的URL存入新数组
           var esrc = image.getAttribute("esrc");
           var newLength = imageUrlsArray.push(esrc);
       });
       //将URL数组回传OC端
       responseCallback({'data':imageUrlsArray})
       }) 
      

      OC端

       [self.bridge callHandler:@"getImageUrlsArray" data:nil responseCallback:^(id responseData) {
           //拿到img的所有url并开始下载
           [self demo_downloadImages:responseData[@"data"]];
       }];
      
    3. 下载图片

       SDWebImageManager *manager = [SDWebImageManager sharedManager];
       //假如没有截取图片则不执行方法
       if (imageUrlArray.count == 0) {
       return;
       }
       //预防数组越界处理
       for (NSUInteger i = 0; i < imageUrlArray.count; i++) {
           [self.downloadImageArray addObject:[NSNull null]];
       }
      
       for (NSUInteger i = 0; i < imageUrlArray.count; i++) {
           NSString *_url = imageUrlArray[i];
           //SDWebImage下载图片
           [manager downloadImageWithURL:[NSURL URLWithString:_url] options:SDWebImageHighPriority progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
           
           if (image) {
               //拿到存储的key
               NSString *key = [manager cacheKeyForURL:imageURL];
               //将url转换成string
               NSString *old = [imageURL absoluteString];
               //根据key拿到本地存储路径
               NSString *localCachePath = [manager.imageCache defaultCachePathForKey:key];
               [self.downloadImageArray removeObjectAtIndex:i];
               [self.downloadImageArray insertObject:localCachePath atIndex:i];
               //将本地缓存图片的地址传到webView
               [self.bridge callHandler:@"imagesDownloadComplete" data:@{@"old":old,@"new":localCachePath} responseCallback:^(id responseData) {
               }];
             }
           }];        }
      
    4. JS端加载图片

       bridge.registerHandler('imagesDownloadComplete',function(data) {
       //解析数据,拿到原来的url和本地存储地址
       var oldUrl = data['old']
       var localCachePath = data['new']
       imagesDownloadComplete(oldUrl,localCachePath)
       })
       var errorNum = 0
       function imagesDownloadComplete(pOldUrl, localCachePath) { 
       //获取所以得img标签
       var allImage = document.querySelectorAll("img");
           allImage = Array.prototype.slice.call(allImage, 0);
           allImage.forEach(function(image) {
       if (image.getAttribute("esrc") == pOldUrl || image.getAttribute("esrc") == decodeURIComponent(pOldUrl)) {
           //加载本地图片地址
           image.src = localCachePath;
           //图片加载错误时再加载
           image.onerror = function(){
               if (errorNum < 5) {
                   errorNum ++;
                   this.src = localCachePath;
               }
               console.log('图片加载从错误重新加载')
            }
        }
       });
       }
      
    5. 点击放大图片
      JS端

       function onImageClick(picUrl){
      
       setupWebViewJavascriptBridge(function(bridge) {
       var allImage = document.querySelectorAll("img[esrc]");
       allImage = Array.prototype.slice.call(allImage, 0);
       var urls = new Array();
       var index = -1;
       var x = 0;
       var y = 0;
       var width = 0;
       var height = 0;
       allImage.forEach(function(image) {
           var imgUrl = image.getAttribute("esrc");
           var newLength = urls.push(imgUrl);
           //获取
           if(imgUrl == picUrl || imgUrl == decodeURIComponent(picUrl)){
               index = newLength-1;
               x = image.getBoundingClientRect().left;
               y = image.getBoundingClientRect().top;
               x = x + document.documentElement.scrollLeft;
               y = y + document.documentElement.scrollTop;
               width = image.width;
               height = image.height;
               console.log("x:"+x +";y:" + y+";width:"+image.width +";height:"+image.height);
      
           }
       });
      
       console.log("检测到点击");
       //将图片在图片数组中的index,frame回传给OC端
       bridge.callHandler('imageDidClicked', {'index':index,'x':x,'y':y,'width':width,'height':height}, function(response) {
           console.log("JS已经发出imgurl和index,同时收到回调,说明OC已经收到数据");
           });
       });
       }
      

    OC端

    图片在UIWebView中的frame和本地存储地址都能拿到,怎么实现放大功能就看自己的喜好了,这里我使用了YYPhotoGroup这个第三方库。

        [self.bridge registerHandler:@"imageDidClicked" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSInteger index = [[data objectForKey:@"index"] integerValue];
        CGFloat originX = [[data objectForKey:@"x"] floatValue];
        CGFloat originY = [[data objectForKey:@"y"] floatValue];
        CGFloat width  = [[data objectForKey:@"width"] floatValue];
        CGFloat height  = [[data objectForKey:@"height"] floatValue];
        
        self.tappedImageView.frame = CGRectMake(originX, originY, width, height);
        self.tappedImageView.image = [YYImage imageWithContentsOfFile:self.downloadImageArray[index]];
        
        responseCallback(@"OC已经收到JS的imageDidClicked了");
        //点击放大图片
        YYPhotoGroupItem *item = [[YYPhotoGroupItem alloc]init];
        item.thumbView = self.tappedImageView;
        item.largeImageURL = [NSURL URLWithString:self.downloadImageArray[index]];
        YYPhotoGroupView *view = [[YYPhotoGroupView alloc]initWithGroupItems:@[item]];
        view.blurEffectBackground = NO;
        [view presentFromImageView:self.tappedImageView toContainer:[UIApplication sharedApplication].keyWindow animated:YES completion:^{
            
        }];
        }];
    

    效果图

    demo.gif

    demo下载https://github.com/YuZhenKao/UIWebView-Demo

    相关文章

      网友评论

          本文标题:使用WebViewJavascriptBridge实现与UIWe

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