0

I'm trying to do a simple task; convert the contents of a UIWebView to a UIImage and save it to the phone's documents directory, however I keep getting a bunch of similar errors every time the following code is run:

  UIGraphicsBeginImageContext(rect.size);
    [self.webView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

Creates the following errors:

Jun 10 20:06:18 <Error>: CGContextSaveGState: invalid context 0x0
Jun 10 20:06:18 <Error>: CGContextSetAlpha: invalid context 0x0
Jun 10 20:06:18 <Error>: CGContextSaveGState: invalid context 0x0
Jun 10 20:06:18 <Error>: CGContextSetFillColorWithColor: invalid context 0x0
Jun 10 20:06:18 <Error>: CGContextAddRect: invalid context 0x0
Jun 10 20:06:18 <Error>: CGContextDrawPath: invalid context 0x0

I did some research and found that this was due to the code above not being in the -(void)drawRect:(CGRect)rect method, however, how do I call this after the UIWebView has been initialised, or do I not call it manually?

This is the code I'm using to create the HTML and load it into the WebView:

-(void)saveHTMLDocument {

    NSMutableString *htmlDocumentToSave = [[NSMutableString alloc] initWithString:@"<html><body>"];
    [htmlDocumentToSave appendString:@"<table border=""1""><tr><th>Snag Number</th><th>Snag Photo</th><th>Snag Description</th><th>Date Taken</th>"];

    for (int i = 0; i < self.fetchedResultsController.fetchedObjects.count; i++) {

    NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation([self.photoArray objectAtIndex:i])];
    NSString *base64String = [imageData base64EncodedString];

    Snag *snag = [self.fetchedResultsController.fetchedObjects objectAtIndex:i];

    NSString *tableString = [NSString stringWithFormat:@"<tr><td>%i</td><td><p><b><img src='data:image/png;base64,%@'></b></p></td><td>%@</td><td>%@</td></tr>", i+1, base64String, snag.snagDescription, snag.dateTaken];
    [htmlDocumentToSave appendString:tableString];

    }

    [htmlDocumentToSave appendString:@"</table></body></html>"];


    //Save the HTMl document that will be attached.
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0];

    NSString *htmlPath = [documentsPath stringByAppendingPathComponent:@"test"];
    [htmlDocumentToSave writeToFile:htmlPath atomically:NO encoding:NSUTF8StringEncoding error:nil];
    self.htmlData = [NSData dataWithContentsOfFile:htmlPath];

    //Generate the UIWebView and load the HTML into it

    NSString *htmlFile = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];

    NSData *htmlData = [NSData dataWithContentsOfFile:htmlFile];

    self.webView = [[UIWebView alloc] init];
    [self.webView loadData:htmlData MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@""]];

    NSString *string = [self.webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('html')[0].innerHTML"];

    NSLog(@"Check if WebView has loaded %@", string);



}

-(void)drawRect:(CGRect)rect {

    UIGraphicsBeginImageContext(rect.size);
    [self.webView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}
jcrowson
  • 4,290
  • 12
  • 54
  • 77

2 Answers2

2

Nope it does not have to be in drawRect, i use this functon

+(UIImage*)captureScreen:(UIView*) viewToCapture
{
    UIGraphicsBeginImageContextWithOptions(viewToCapture.bounds.size, viewToCapture.opaque, 0.0);
    [viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return viewImage;
}

In my app and it correctly takes a screen shot of my WebView

Omar Abdelhafith
  • 21,163
  • 5
  • 52
  • 56
  • This technique was flaky in older versions of iOS when used with UIWebViews, although I haven't tried it recently. Another potential issue is trying to figure out when the UIWebView has completed displaying the content. – EricS Jun 10 '12 at 19:55
  • Yes indeed, i tend to delay it from 300 - 500 milliseconds after the webview has finished loading, i use this in iOS 4.3 and 5.1 and it works good, i currently use it to animate page transitions in an ePub viewer and it works very good – Omar Abdelhafith Jun 10 '12 at 19:56
  • Thanks Omar, one quick question, how can I call that method? – jcrowson Jun 10 '12 at 20:07
  • i call it like this pageView.pageImage = [Utilities captureScreen:webView]; Where pageImage is a UIImage Utilities is a utility class that i add class methods only to it Also, please consider upvoting and/or accepting answer if it was helpful :) – Omar Abdelhafith Jun 10 '12 at 20:11
  • @OmarAbdelhafith I'm still getting an CGContextSaveGState: invalid context 0x0 using your method. – jcrowson Jun 10 '12 at 20:34
  • But does the view get drawn or not? – Omar Abdelhafith Jun 10 '12 at 20:38
  • With UIWebViewDelegate, you will get an alert when the web view has finished loading. I use this to time my page transitions. – borrrden Jun 11 '12 at 03:18
  • indeed, webViewDidFinishLoad will get called when loading is done – Omar Abdelhafith Jun 11 '12 at 05:01
1

What is rect.size? If the width or height are zero, then CG will be unable to create a context, leading to the sorts of errors that you're seeing.

@OmarAbdelhafith is correct about the rest -- you don't have to do this work in -drawRect:, and the rest of your code looks OK. Also, make sure you are running this code after the UIWebView is fully loaded.

Kurt Revis
  • 27,695
  • 5
  • 68
  • 74