Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could I trick [CALayer renderInContext: ] to render only a section of the layer?

I'm well aware that there's no nice way to render a small part of a UIView to an image, besides rendering the whole view and cropping. (VERY expensive on something like an iPad 3, where the resulting image is huge). See here for a description of the renderInContext method (there's no alternatives).

The part of the view that I want to render can't be predetermined, meaning I can't set up the view hierarchy for the small section to be it's own UIView (and therefore CALayer).

My Idea...

I had an idea, but I need some direction if I'm going to succeed. What if I create a category on UIView (or CALayer) that adds a method along the lines of:

[UIView renderSubframe:(CGFrame)frame];

How? Well, I'm thinking that if a dummy view the size of the sub frame was created, then the view could be shifted temporarily onto this. Then the dummy view's layer could call renderInContext:, resulting in an efficient and fast way of getting views.

So...

I'm really not that up to speed with CALayer/Quartz core stuff... Will this have any chance of working? If not, what's another way I could achieve the same thing - what's the most efficient way I could achieve the same result (or has anyone else already faced this problem and come up with a different solution)?

like image 283
Jordan Smith Avatar asked Jan 05 '13 11:01

Jordan Smith


2 Answers

Here's the category I ended up writing. Not as hard as I first thought, thanks to a handy CGAffineTransform.

#import "UIView+RenderSubframe.h"
#import <QuartzCore/QuartzCore.h>

@implementation UIView (RenderSubframe)

- (UIImage *) renderWithBounds:(CGRect)frame {

    CGSize imageSize = frame.size;

    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
    CGContextRef c = UIGraphicsGetCurrentContext();

    CGContextConcatCTM(c, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y));
    [self.layer renderInContext:c];

    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return screenshot;

}

@end
like image 85
Jordan Smith Avatar answered Oct 25 '22 18:10

Jordan Smith


A couple of different ways you might do this:

  1. specify a CGContextClipToRect for your context before calling renderInContext;

  2. use:

setNeedsDisplayInRect:

Marks the region within the specified rectangle as needing to be updated.

     - (void)setNeedsDisplayInRect:(CGRect)theRect

This would make the rendering happen only in the specified rect. I am not sure though whether this would work as per your requirement.

I think that the first option should work seamlessly.

like image 27
sergio Avatar answered Oct 25 '22 17:10

sergio