使用WebViewJavascriptBridge實現(xiàn)與UIWebView的深度交互

最近在做的項目遇到這么一個需求:網(wǎng)絡(luò)加載一段HTML格式的只有body部分的文本,自己本地拼寫完整的HTML,圖片需帶點擊放大瀏覽功能(移動端實現(xiàn))。
分析需求之后發(fā)現(xiàn)難點在如何與UIWebview進(jìn)行交互,在查閱資料后,找到http://kittenyang.com/webview-javascript-bridge
KittenYang大神完美解決了我的問題,但過程中還是出了不少問題,這里就寫下按照大神的思路寫的具體實現(xiàn)過程
這里主要用了WebViewJavascriptBridge,SDWebImage,YYPhotoGroup這三個第三方庫。

WebViewJavascriptBridge使用簡介

HTML文本的<script></script>標(biāo)簽中間添加如下代碼

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) {
    //方法名,傳遞的參數(shù),回調(diào)
    bridge.callHandler('testObjcCallback', 'Hello world', function(response)){
    };
    //方法名,收到的參數(shù),回調(diào)方法
    bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
     var message = data['foo']
     console.log(message)
})

在OC端需實現(xiàn)
@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");
}];
//調(diào)用JS的方法并傳參
[_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }];

完成上面的步驟后就可以實現(xiàn)與UIWebView的JS交互,但是要實現(xiàn)需求中所提到的功能還需要更深層次的交互,還需要一定的前端知識。

流程設(shè)計

流程圖

功能實現(xiàn)

  1. HTML文本處理

     //獲取本地HTML路徑
     NSString* HTMLPath = [[NSBundle mainBundle] pathForResource:@"HTMLDemo" ofType:@"html"];
     //獲取HTML字符串
     NSMutableString* HTMLString = [NSMutableString stringWithContentsOfFile:HTMLPath encoding:NSUTF8StringEncoding error:nil];
     //設(shè)定需要替換的字符串
     NSRange range = [HTMLString rangeOfString:@"<P>mainviews</P>"];
     //將字符串中的src標(biāo)簽都替換掉,防止加載HTML的時候預(yù)加載圖片
     NSString *replace = [self.HTMLBoday stringByReplacingOccurrencesOfString:@"src=" withString:@"esrc="];
     //將字符串替換調(diào)
     [HTMLString replaceOccurrencesOfString:@"<P>mainviews</P>" withString:replace options:NSCaseInsensitiveSearch range:range];
     //根據(jù)正則表達(dá)式查找img標(biāo)簽
     NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(<img[ ^>]+esrc=\")(\\S+)\"" options:0 error:nil];
     //給img標(biāo)簽添加點擊事件
     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標(biāo)簽
     var allImage = document.querySelectorAll("img");
     //Array.prototype.slice.call(arguments)能將具有l(wèi)ength屬性的對象轉(zhuǎn)成數(shù)組
     allImage = Array.prototype.slice.call(allImage, 0);
     var imageUrlsArray = new Array();
     allImage.forEach(function(image) {
         //將所有圖片的URL存入新數(shù)組
         var esrc = image.getAttribute("esrc");
         var newLength = imageUrlsArray.push(esrc);
     });
     //將URL數(shù)組回傳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];
     //假如沒有截取圖片則不執(zhí)行方法
     if (imageUrlArray.count == 0) {
     return;
     }
     //預(yù)防數(shù)組越界處理
     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轉(zhuǎn)換成string
             NSString *old = [imageURL absoluteString];
             //根據(jù)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) {
     //解析數(shù)據(jù),拿到原來的url和本地存儲地址
     var oldUrl = data['old']
     var localCachePath = data['new']
     imagesDownloadComplete(oldUrl,localCachePath)
     })
     var errorNum = 0
     function imagesDownloadComplete(pOldUrl, localCachePath) { 
     //獲取所以得img標(biāo)簽
     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("檢測到點擊");
     //將圖片在圖片數(shù)組中的index,frame回傳給OC端
     bridge.callHandler('imageDidClicked', {'index':index,'x':x,'y':y,'width':width,'height':height}, function(response) {
         console.log("JS已經(jīng)發(fā)出imgurl和index,同時收到回調(diào),說明OC已經(jīng)收到數(shù)據(jù)");
         });
     });
     }
    

OC端

圖片在UIWebView中的frame和本地存儲地址都能拿到,怎么實現(xiàn)放大功能就看自己的喜好了,這里我使用了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已經(jīng)收到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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容