John Scalo
John Scalo

Reputation: 3401

Rendering UIView to image when view draws outside its bounds

I want to render a UIView to an image. My go-to UIView category for this is

- (UIImage *)num_renderToImage
{
    UIGraphicsBeginImageContext(self.bounds.size);

    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

However in this case, the UIView has elements that draw outside its bounds and the above clips them. Altering the size passed to UIGraphicsBeginImageContext doesn't help since the size grows down and to the right, but these elements are above and to the left. What's the right way to do this?

Upvotes: 3

Views: 1400

Answers (2)

Jerem Lachkar
Jerem Lachkar

Reputation: 1197

I made this more general version in Swift 5.0 if anyone has the problem. Just set the offset to the value you want :

private func snapshotView(cell:UIView) -> UIImageView
{
        // by how much extra point out of the view bounds you want to draw your snapshot 
        let offset:CGFloat = 10
        

        let frame = cell.bounds.union(CGRect(x:-offset * 2.0,y:-offset * 2.0,
                          width:cell.bounds.width,
                          height: cell.bounds.height)).size 
        UIGraphicsBeginImageContextWithOptions(frame, false ,0)
        let context = UIGraphicsGetCurrentContext()
        context!.translateBy(x: offset, y: offset);
        cell.layer.render(in: context!)
        let snapshotImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
       
        return UIImageView(image: snapshotImage)
}

Upvotes: 0

Andrew Wooster
Andrew Wooster

Reputation: 86

In the scenario above, with a UIView clipping a UIButton that draws outside its bounds, you might try:

- (IBAction)snapshot:(id)sender {

    UIButton *button = sender;
    UIView *v = button.superview;

    // Prepare the rectangle to be drawn
    CGRect allTheViews = CGRectUnion(v.bounds, button.frame);

    UIGraphicsBeginImageContext(allTheViews.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // This is what you differently
    CGContextTranslateCTM(context, -allTheViews.origin.x, -allTheViews.origin.y);

    // This part is the same as before
    [v.layer renderInContext:context];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [UIImagePNGRepresentation(img) writeToFile:@"/tmp/foo.png" atomically:NO];
}

Here we're taking the union of what we want to draw, then translating the CTM so it's in view in the graphics context we're drawing into.

(This example, in particular, is hooked up to the action of the button and writes the UIImage of the button and containing view out to a file. You can adjust as your needs require.)

Upvotes: 5

Related Questions