9

I have a UIImageView with zillions of views. Some of these views have layer shadows or glow. This view is slightly bigger than the device screen.

This view is basically a big transparent view that contains many objects (images, buttons, etc.)

Now I want to flatten everything on that view to a UIImage. Then, I do:

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

result is equal to a totally transparent image, but has the correct size.

am I missing something?

thanks

Duck
  • 34,902
  • 47
  • 248
  • 470
  • This code works: http://stackoverflow.com/questions/3129352/need-to-capture-uiview-into-a-uiimage-including-all-subviews – AlexeyVMP Feb 18 '13 at 15:19
  • This core really works: http://stackoverflow.com/questions/3129352/need-to-capture-uiview-into-a-uiimage-including-all-subviews – AlexeyVMP Feb 18 '13 at 15:21

2 Answers2

19

In Apple's sample code, they adjust the geometry of the graphics context to match the geometry of the layer before calling renderInContext:.

They're dealing with a window, but it looks like the code should work with any view with some minor changes.

I've not tried building this, but here's my attempt to change Apple's code to work on any view.

- (UIImage*)imageFromView:(UIView *)view 
{
    // Create a graphics context with the target size
    // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
    // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
    CGSize imageSize = [view bounds].size;
    if (NULL != UIGraphicsBeginImageContextWithOptions)
        UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
    else
        UIGraphicsBeginImageContext(imageSize);

    CGContextRef context = UIGraphicsGetCurrentContext();

    // -renderInContext: renders in the coordinate space of the layer,
    // so we must first apply the layer's geometry to the graphics context
    CGContextSaveGState(context);
    // Center the context around the view's anchor point
    CGContextTranslateCTM(context, [view center].x, [view center].y);
    // Apply the view's transform about the anchor point
    CGContextConcatCTM(context, [view transform]);
    // Offset by the portion of the bounds left of and above the anchor point
    CGContextTranslateCTM(context,
                          -[view bounds].size.width * [[view layer] anchorPoint].x,
                          -[view bounds].size.height * [[view layer] anchorPoint].y);

    // Render the layer hierarchy to the current context
    [[view layer] renderInContext:context];

    // Restore the context
    CGContextRestoreGState(context);

    // Retrieve the screenshot image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return image;
}
Hima
  • 1,249
  • 1
  • 14
  • 18
Kris Markel
  • 12,142
  • 3
  • 43
  • 40
  • what I need is not exactly a screenshot... or rendering a window, because I need to render specifically what is on one view and not in all. I was expecting the layer property to contain everything that is visible on a given UIView but strangely it doesn't. Thanks. – Duck Oct 06 '10 at 14:42
  • I think you can change the code to just operate on the view you want. The window bits are incidental. – Kris Markel Oct 06 '10 at 15:21
  • What can I do to ensure that the hidden portion is also included? Currently it cut off on the view bound. P.S.: I am having a tableView – Rahul Choudhary Jul 14 '12 at 16:09
  • This code does not care about subview - it does not work with UIView. Still getting empty image without subviews... – AlexeyVMP Feb 18 '13 at 15:15
0

Here's a general version (Swift 2.x) to flatten an array of UIViews into a single UIImage. In your case, just pass an array consisting of a single UIView, and it should work.

// Flattens <allViews> into single UIImage
func flattenViews(allViews: [UIView]) -> UIImage? {
    // Return nil if <allViews> empty
    if (allViews.isEmpty) {
        return nil
    }

    // If here, compose image out of views in <allViews>
    // Create graphics context
    UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, UIScreen.mainScreen().scale)
    let context = UIGraphicsGetCurrentContext()
    CGContextSetInterpolationQuality(context, CGInterpolationQuality.High)

    // Draw each view into context
    for curView in allViews {
        curView.drawViewHierarchyInRect(curView.frame, afterScreenUpdates: false)
    }

    // Extract image & end context
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    // Return image
    return image
}
Crashalot
  • 33,605
  • 61
  • 269
  • 439