Search code examples
iospdfuiviewcalayeruigraphicscontext

iOS: Single-page PDF from multiple UIViews


I wrote this code snippet that takes an array of UIViews and renders them into a PDF. However, each view is being rendered into a separate page because I can't figure out how to merge PDF pages or render all views into a single page.

Current code:

+ (NSData*)pdfFromViews:(NSArray*)views
{
    NSMutableData* pdf = [NSMutableData data];

    UIGraphicsBeginPDFContextToData(pdf, CGRectZero, nil);
    CGContextRef context = UIGraphicsGetCurrentContext();

    for(UIView* view in views)
    {
        UIGraphicsBeginPDFPageWithInfo(view.bounds, nil);
        [view.layer renderInContext:context];
    }

    UIGraphicsEndPDFContext();

    return pdf;
}

If I only call this line once - UIGraphicsBeginPDFPage() - the views get drawn on top of each other, no matter what changes I make to view.layer.frame or what size CGRect I use for my context.

I'm happy with the current image quality and don't need to add any links or text into the PDF.

I'm looking for a way to merge my resultant PDF pages, or draw my views into a single page. NOTE: I can't add my views as subviews into a common parent view because it messes up my app layout. Ideally, I would do this but UIViews can't be copied.


Solution

  • Shift the origin between renders with CGContextTranslateCTM:

    + (NSData*)pdfFromViews:(NSArray*)views {
        NSMutableData* pdf = [NSMutableData data];
        UIGraphicsBeginPDFContextToData(pdf, CGRectZero, nil);
        UIGraphicsBeginPDFPage();
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        for(UIView *view in views) {
            [view.layer renderInContext:context];
            CGContextTranslateCTM(context, 0, view.bounds.size.height);
        }
    
        UIGraphicsEndPDFContext();
        return pdf;
    }
    

    You may also want to specify a page size that fits your views. Calculate the size and call UIGraphicsBeginPDFPageWithInfo instead of UIGraphicsBeginPDFPage.