Get UIWebView content and convert it to image

2019-08-02 01:13发布

问题:

I'm using a UIWebView. I call a web service to get the content for that. In web service response I get HTML string as below:

<div style='text-align: center;'> <div style=' display: inline-block; width: 120px;height: 120px; text-align:center; '; ><img  src='http://assetdetailsdemo.azurewebsites.net/QRUpload/4774.jpg' style='height:100px; width:100px' /><br /><div>4774</div></div> <br /><br /></div>

This is just an example. I may get 'n' number of above kind of 'div' tags which may have images 1 in each. I have load this in UIWebView using:

[_webView loadHTMLString:_HTMLStr baseURL:[[NSBundle mainBundle] bundleURL]];

where _HTMLStr contains above HTML string.
My main issue is I have a requirement to get hole content of UIWebView and need to generate an image. I have already tried below code to get the image:

UIGraphicsBeginImageContext(_webView.bounds.size);
[_webView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

This method avail me to convert the content of UIWebView to UIImage. But, I want a pice of code which allow me to generate image only from the content of webview. Or we can say that I'm finding another method to get the image.

回答1:

So I've experienced the same issue here. After a while struggling with it, I found out that:

  1. loadHTMLString:baseURL: Doesn't actually load the string. It'll perform lots of stuff then callback in the delegate webViewDidFinishLoad:(UIWebView *)webView

  2. You'll have to keep the VC until it rendered the HTML. Otherwise, the VC will be disposed before the calling back delegate webViewDidFinishLoad:. In my case, I use singleton object to retain it.

Solution:

  1. Create singleton object of the VC.
  2. Set webView.delegate = VC
  3. Implement the image rendering in webViewDidFinishLoad:(UIWebView *)webView
  4. You can store a callback or a delegate or something to notify back that the webView has completely rendered.

EDIT:

Because loadHTML doesn't actually load right away, but perform something (not sure what it does tho...) then callback when complete in webViewDidFinishLoad:(UIWebView *)webView, therefore you'll need to wait until that method got called.

So, you declare webView and callbackBlock in your VC (or any object, depend on you):

@interface MyObject () <UIWebViewDelegate>

@property (strong, nonatomic) UIWebView *webView;
@property (strong, nonatomic) void(^callbackBlock)(UIImage *generatedImage);

@end

So when you call generateImageFromHTML:(NSString *)html callback:(callbackBlock)callback, it'll wait until the image completely generated then callback.

You'll need something to retain the webView and callbackBlock. In my case, I just use singleton to keep it.

+ (instanceType)loadHTML:(NSSTring *)html callBack:(callbackBlock)callback {
    static dispatch_once_t onceToken;
    static MyObject *instance;
    // Keep reference
    dispatch_once(&onceToken, ^{
        instance = [MyObject new];

        instance.webView = [UIWebView new];
        instance.webView.delegate = instance;
        instance.webView.scalesPageToFit = YES;
    });

    // Perform load here
    [instance.webView loadHTMLString:html baseURL: [[NSBundle mainBundle] bundleURL]];

    // Wait until done
    instance.callbackBlock = callback;
    return instance;
}

Next is actual create the image from htmlContent image and return it:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    if (self.callbackBlock) {
        self.callbackBlock([self imageFromWebView:webView]);
    }
}

- (UIImage *)imageFromWebView:(UIWebView *)view
{
    // Adjust the image size to fit webContent
    // get the width and height of webpage using js (you might need to use another call, this doesn't work always)
    // CGFloat contentWidth = [view stringByEvaluatingJavaScriptFromString:@"document.getElementsByClassName(\"content\")[0].offsetWidth"].floatValue;
    // CGFloat contentHeight = [view stringByEvaluatingJavaScriptFromString:@"document.getElementsByClassName(\"content\")[0].offsetHeight"].floatValue;
    CGSize documentSize = CGSizeMake(contentWidth, contentHeight);

    view.frame = CGRectMake(0, 0, contentWidth, contentHeight);

    //make the snapshot
    UIGraphicsBeginImageContext(documentSize);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}