3

I am trying to convert my UIView into UIImage using below code.

+ (UIImage *) imageWithView:(UIView *)view{
     float scale = 1.0f;
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, scale);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    view.layer.contents = nil;
    return img;
}

There are two problem with this code.

1. When I run this code in background thread(!mainThread)

I faced memory leak problem when renderInContext is called in background thread.

2. When I run this code on Main thread

There is no memory leak but on iPad 3 I am facing some performance issue (my UI hangs when this method is called) while creating image from UIView. As I need to call this function more than 5 times in a seconds so UI hangs gives very bad user experience.

Please guide me if I am doing some thing wrong here?

Madhu S. Kapoor
  • 345
  • 1
  • 5
  • 11

2 Answers2

8

I think that issue 1 is related to the fact that UIKit is not thread safe and its use will lead to all kinds of side effects.

If you have performance issue like you are describing, the only path forward I see is directly using CoreGraphics (not UIKit) on a secondary thread.

You might try something like this, as a start:

size_t width = view.bounds.size.width;
size_t height = view.bounds.size.height;

unsigned char *imageBuffer = (unsigned char *)malloc(width*height*4);
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();

CGContextRef imageContext =
    CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace,
              kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);

CGColorSpaceRelease(colourSpace);

[view.layer renderInContext:imageContext];

CGImageRef outputImage = CGBitmapContextCreateImage(imageContext);

CGImageRelease(outputImage);
CGContextRelease(imageContext);
free(imageBuffer);

As you see, this is pretty more complex than the UIKit way, but it can be run on a secondary thread (provided you find a way to pass the outputImage back to your UI thread which is not shown).

sergio
  • 68,819
  • 11
  • 102
  • 123
  • Have you seen my sample code? `outputImage` is a CGImageRef which you can use to create a `UIImage` through `imageWithCGImage:` -- but you should do this last step on the main thread... – sergio Oct 11 '12 at 17:54
  • I think, there is confusion, I want to convert UIView to UIImage. – Madhu S. Kapoor Oct 11 '12 at 18:17
  • ouch... you are right... please, see my edited code. this should do it. – sergio Oct 11 '12 at 18:30
  • 1
    I didn't know this is how you start a image context, cool stuff! However, post iOS 4, UIGraphicsBeginImageContext, et al., are all thread safe and not limited to the main thread anymore. See the notes: https://developer.apple.com/library/ios/documentation/uikit/reference/UIKitFunctionReference/Reference/reference.html#jumpTo_37 – Mr. T Jul 09 '14 at 23:33
  • 1
    Great! I noticed that created image is flipped vertically. – Krešimir Prcela Jul 21 '15 at 13:49
  • 2
    To flip the image back to normal add these 2 lines after the CGColorSpaceRelease: `CGContextTranslateCTM(imageContext, 0, height); CGContextScaleCTM(imageContext, 1.0f, -1.0f);` – Krešimir Prcela Jul 22 '15 at 10:52
2

I just had this happen to me (memory leak due to renderInContext) on the main thread. I was looping over hundreds off-screen views, rendering them to UIImage objects, and saving them as PNG files. What solved the problem for me was wrapping the guts of my loop in an @autoreleasepool block:

Broken:

for (...) {
   ...render layer in context...
   ...save image to disk...
}

Works:

for (...) {
   @autoreleasepool {
      ...render layer in context...
      ...save image to disk...
   }
}

Makes sense, right?

Todd Lehman
  • 2,880
  • 1
  • 26
  • 32