Jordan Smith Jordan Smith - 9 months ago 47
iOS Question

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.


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)?

Answer Source

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();
    return screenshot;