The deep interaction between UIWebView and JS

Source: Internet
Author: User

I want to implement a requirement: to display a string of Web-based text that is only body part of the network, in accordance with the local CSS file, you need to spell the full HTML yourself. In addition, you also need to disable the acquisition of HTML text in the < IMG > tag auto-loading, and the operation of the download image is placed on the native side to process, and through JS to the image in the cache address to UIWebView.

The reason to put the picture operation on the native side is the advantage: 1, can be local cache, the next time to enter this article can be read directly from the cache, improve response speed and save user traffic. 2, can achieve click on the image to enlarge, save pictures to albums and other operations.

Technical difficulties also have two: 1, how to let the HTML text onload, disable their own image loading, but to get pictures from the local? 2. How to return the downloaded pictures of native to the webpage?

At first, I was also helpless, look at the document can only find a - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script simple interaction with JS method, failed to do so. Until I saw Webviewjavascriptbridge on GitHub, this wrapper library for Uiwebview/webviews and JS interaction.

Just read sample When I almost did not get dizzy, good memory than rotten pen, I never hide their stupidity, so I drew a diagram. Let's look at the code before we put it on the map.

At first, we initialize the native and JS sides separately:

OC End:

@property WebViewJavascriptBridge* bridge;

The corresponding initialization code is as follows, which directly includes a callback to receive JS in the initialization:

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {          NSLog(@"ObjC received message from JS: %@", data);        responseCallback(@"Response for message from ObjC");}];

JS End: (The following is a fixed notation, your own JS file must contain the following code)

function connectWebViewJavascriptBridge(callback) {      if (window.WebViewJavascriptBridge) {        callback(WebViewJavascriptBridge)    } else {        document.addEventListener(‘WebViewJavascriptBridgeReady‘,   function() {            callback(WebViewJavascriptBridge)        }, false)    }}connectWebViewJavascriptBridge(function(bridge) {      bridge.init(function(message, responseCallback) {        log(‘JS got a message‘, message)        var data = { ‘Javascript Responds‘:‘Wee!‘ }        log(‘JS responding with‘, data)        responseCallback(data)    })}    

Then, we need to know that in, there are WebViewJavascriptBridge only two ways of interacting: send and callHandle , JS and OC have both methods, so the corresponding four kinds of relationships are:

The interpretation of the corresponding relationship in the above table is, for example, the first: in JS if the call bridge.send() , then the OC end _bridge initialization method of the callback .

Similarly, the second, called in JS bridge.callHandler(‘testJavascriptHandler‘) , it will trigger the OC-side registration of the same name method:

bridge.registerHandler(‘testJavascriptHandler‘, function(data, responseCallback) {          log(‘ObjC called testJavascriptHandler with‘, data)        var responseData = { ‘Javascript Says‘:‘Right back atcha!‘ }        log(‘JS responding with‘, responseData)        responseCallback(responseData)})

Understanding the rules of use, here's a look at the overall approach to our practical needs:

--1--

First of all, the first step we have to make is to replace the obtained HTML text by default src , to prevent it from automatically loading the picture.

NSString *_content = [contentstring stringByReplacingOccurrencesOfString:@"src" withString:@"esrc"];
--2--

Because we get only the body part of the HTML, we need to write the full HTML ourselves.

We make <body onload="onLoaded()"> the time to call the function in JS onLoaded() . In this function we iterate through all the IMG tags esrc , save them as an array and return them to the OC side, and let the native side download the images.

function onLoaded() {    connectWebViewJavascriptBridge(function(bridge) {        var allImage = document.querySelectorAll("img");        allImage = Array.prototype.slice.call(allImage, 0);        var imageUrlsArray = new Array();        allImage.forEach(function(image) {            var esrc = image.getAttribute("esrc");            var newLength = imageUrlsArray.push(esrc);        });        bridge.send(imageUrlsArray);    });}
--3--

bridge.sendTriggers the handler in the Webviewjavascriptbridge initialization method + (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate handler:(WVJBHandler)handler; , and we download all the images in the handler block. and return the downloaded image to the address in the cache to a JS.

#pragma mark--Download all images-(void) Downloadallimagesinnative: (Nsarray *) imageurls{sdwebimagemanager *manager = [Sdwebimagem    Anager Sharedmanager]; Initializes an array of empty elements _allimagesofthisarticle = [Nsmutablearray arraywithcapacity:imageurls.count];//a local array for saving all pictures for (N Suinteger i = 0; i < imageurls.count-1;    i++) {[_allimagesofthisarticle addobject:[nsnull null]];        } for (Nsuinteger i = 0; i < imageurls.count-1; i++) {NSString *_url = imageurls[i]; [Manager Downloadimagewithurl:[nsurl Urlwithstring:_url] options:sdwebimagehighpriority progress:nil completed:^ ( UIImage *image, Nserror *error, Sdimagecachetype CacheType, BOOL finished, Nsurl *imageurl) {if (image) {D Ispatch_async (Dispatch_get_global_queue (dispatch_queue_priority_default, 0), ^{nsstring *imgB64 = [UII                    Magejpegrepresentation (Image, 1.0) Base64encodedstringwithoptions:nsdatabase64encoding64characterlinelength];           Return the image to JS in the disk address         NSString *key = [manager Cachekeyforurl:imageurl];                    NSString *source = [NSString stringwithformat:@ "data:image/png;base64,%@", imgB64];                [_bridge callhandler:@ "Imagesdownloadcomplete" Data:@[key,source]];            });    }        }]; }}
--4--

[_bridge callHandler:@"imagesDownloadComplete" data:@[key,source]]Will trigger JS in the function imagesDownloadComplete() . Iterate through all the IMG tags in this function and assign the image address you sent to the IMG src.

function imagesDownloadComplete(pOldUrl, pNewUrl) {      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 = pNewUrl;        }    });}

At this point, through WebViewJavascriptBridge the processing UIWebView and JS Interactive implementation of the local processing of the download of the Web page picture is basically completed. This example shows a complete process, basically involving JS and OC of various interactions including OC call JS, JS call oc and so on. If you have other business needs, and basically follow this process can be leaf out, the only difference is the business logic.

Let me give you one more example. Also appear in my business needs, is to click on the picture on the page, the image will be zoom-out animation to enlarge, left and right to see other pictures, but also need to double-click to enlarge View, save pictures and other functions. similar to this:

At first glance, we are clicking on a picture on a webpage, how can we let this image jump out of the picture alone? And you can swipe left and right to show other pictures?

First of all, we still need to transform the Web to get the HTML text, the regular match , plus onClick events, binding a JS method, and the ESRC as a parameter into the binding method.

//正则替换NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(]+esrc=\")(\\S+)\"" options:0 error:nil];   result = [regex stringByReplacingMatchesInString:newContent options:0 range:NSMakeRange(0, newContent.length) withTemplate:@"

JS in the onImageClick() function. The main task of this function is to get the number of the clicked Image in all pictures and the position in the current screen. and return this information to OC.

function Onimageclick (picurl) {Connectwebviewjavascriptbridge (function (bridge) {var allimage = Document.queryse        Lectorall ("P 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 ("detected clicks"); Bridge.callhandler (' imagedidclicked ', {' index ': index, ' x ': x, ' y ': y, ' width ': width, ' height ': height}, function (        Response) {Console.log ("JS has issued Imgurl and index, and has received a callback stating that OC has received the data");    }); });}

Bridge.callhandler triggers the [_bridge registerhandler:@ "imagedidclicked" handler:^ in OC (ID data, Wvjbresponsecallback responsecallback) {}] . We can get the handler of the click image in all the pictures, and click on the picture in the current picture space position. To achieve the effect of clicking on the picture zoom-out, we should be good at "cheating". The picture in the webpage can't "jump" to enlarge, but we can make the position information of x, y, Width and height according to JS, create a uiimageview,image and the current click picture consistent, set the transparency to 0,add to UIWebView above. And through the Idmphotobrowser this open source library to achieve picture browsing.

    [_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];        tappedImageView.alpha = 0;        tappedImageView.frame = CGRectMake(originX, originY, width, height);        tappedImageView.image = _allImagesOfThisArticle[index];//_allImagesOfThisArticle是一个本地数组用来存放所有图片        NSLog(@"OC已经收到JS的imageDidClicked了: %@", data);        responseCallback(@"OC已经收到JS的imageDidClicked了");        //点击放大图片        [self presentPhotosBrowserWithInitialPage:index animatedFromView:tappedImageView];    }];
Tips

Because I use sublime Text, so I can not do JS debugging. If you want to debug with atom, it feels a little overkill. I just want a place where I can easily see if it's printed console.log or if the JS function is called. Always believe that any problem can be solved. We can use Safari.

Connect your iphone or use the simulator, when your program currently shows a Uiwebview,safari will automatically recognize this uiwebview, and can 开发 find your device in the menu bar to debug.

Select 控制台 , you can see the long-Lost debugging window and JS of the console.log .

The above is an example of using the WebViewJavascriptBridge deep interaction between UIWebView and JS.

Toss Webviewjavascriptbridge these days down, my greatest feeling is, learning. Think of node. JS can write server, React native can develop iOS, and GitHub is the most popular language is JS, which led me to a little want to learn JS. But I calmed down again, clear my main task what, think Swift is not thoroughly understand, which have time to engage in front-end. According to my experience, let me sleep, I guess tomorrow is not the idea. ^ ^

The deep interaction between UIWebView and JS

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.