Very technical and short today. Did you know that it's super dead easy to create a PDF from the contents of a UIWebView by just using built in Cocoa technologies? Turns out, it is!

Use cases for this might be the generation of forms, receipts, bills or anything a like. I used the code to create order confirmations a while back and right now I'm just using it to output some stats. It's handy, robust and also App Store compliant.

What you need to create is a PDFPrintPageRenderer that subclasses UIPrintPageRenderer, a class that certainly qualifies as a 'hidden gem'. Then it's just a matter of overriding a few methods and, well, there you go.

import UIKit

class PDFPrintPageRenderer: UIPrintPageRenderer {
    var pdfMode = false

    override var paperRect: CGRect {
        get {
            if (!pdfMode){
                return super.paperRect
            } else {
                return UIGraphicsGetPDFContextBounds()
            }

        }
    }

    override var printableRect: CGRect {
        get {
            if (!pdfMode){
                return super.printableRect
            } else {
                return self.paperRect.insetBy(dx: 20, dy: 20)
            }

        }
    }


    func generatePDFData () -> NSData {
        self.pdfMode = true
        let pdfData = NSMutableData()

        UIGraphicsBeginPDFContextToData(pdfData, CGRectMake(0,0,595,842), nil);

        self.prepareForDrawingPages(NSMakeRange(0, 1))
        let bounds = UIGraphicsGetPDFContextBounds()

        for i in 0..<self.numberOfPages() {
            UIGraphicsBeginPDFPage()
            self.drawPageAtIndex(i, inRect: bounds)
        }

        UIGraphicsEndPDFContext()

        self.pdfMode = false

        return pdfData
    }

}

This is part one of the whole story. The second part is where you actually generate PDF output from a UIWebView. This is even simpler as you can see below. Always call the code in the didFinishLoading delegate callback of UIWebView.


    func webViewDidFinishLoad(webView: UIWebView) {
        let renderer = PDFPrintPageRenderer()
        let formatter = webView.viewPrintFormatter()
        renderer.addPrintFormatter(formatter, startingAtPageAtIndex: 0)

        formatter.maximumContentHeight = 1000
        formatter.maximumContentWidth = 1000

        let data = renderer.generatePDFData()

        data.writeToFile("/tmp/test.pdf", atomically: true)
    }

Simple.