derdida
derdida

Reputation: 14904

Generate PDF with Swift

I would like to create a PDF from a UITableView in Swift. Ill found some tutorials for Objective C, and tried it out but there is still no file generated by this code.

        // get a temprorary filename for this PDF

        var path = NSTemporaryDirectory();
        var pdfFilePath = path.stringByAppendingPathComponent("mypdfdocument.pdf")

        UIGraphicsBeginPDFContextToFile(pdfFilePath, CGRectMake(0, 0, self.tableView.contentSize.width, self.tableView.contentSize.height), nil)
        UIGraphicsBeginPDFPage();
        self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
        self.tableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: false)
        self.tableView.layer.renderInContext(UIGraphicsGetCurrentContext())

        var screensInTable = Int(self.tableView.contentSize.width) / Int(self.tableView.contentSize.height)

        for i in 1...screensInTable {

            var point = CGFloat(CGFloat(i) * self.tableView.bounds.height)
            var contentOffset:CGPoint = CGPointMake(0, point)
            self.tableView.setContentOffset(contentOffset, animated: false)
            self.tableView.layer.renderInContext(UIGraphicsGetCurrentContext())
        }

        UIGraphicsEndPDFContext();

And: I have a Table with 4 Sections and different Row Heights and Cell Templates. Is there a Chance that the generated PDF easily with this type of code? Or would it be better to create Row by Row with CoreText?

Thanks in advance.

Upvotes: 8

Views: 19074

Answers (1)

mustafa
mustafa

Reputation: 15464

Here is my approach. Use a template view. It has all the static texts and images. And then for every page in pdf use this template view. Here is the function that I use:

func renderAsPDF(demandEntry: ParsedDemandEntry, inView view: UIView) -> NSData? {
    let entries = demandEntry.demands
    let pageCount = Int(ceil(Double(entries.count) / Double(demandCountForPage)))
    if pageCount != 0 {
        let views = (1...pageCount).map { (pageNumber: Int) -> UIView in
            let pdfPageView = createTemplatePageViewWithParsedEntry(demandEntry, inView: view)

            let pageRange = ((pageNumber - 1) * demandCountForPage)..<(min(pageNumber * demandCountForPage, entries.count))
            let entriesForPage = Array(entries[pageRange])

            addEntries(entriesForPage, toView: pdfPageView)

            pdfPageView.removeFromSuperview()

            return pdfPageView
        }

        return toPDF(views)
    } else {
        return nil
    }
}

The ParsedDemandEntry is my model object. The view parameter is a container view to prepare pdf view in it. This is necessary because I use auto layout to position all labels in pdf view. Without a super view layout process won't work.

Let's walk into function. First I get entries from model object. Think these are rows that needs to populate in pdf. After that I calculate how many pages I need. Then the loop begins. In every loop I create a tamplate view. (pdfPageView). And then fill it with entries. (addEntries(_:toView:) function call).

After the loop I give all views to toPDF function. It creates NSData that represents pdf. Here is how toPDF function look like:

private func toPDF(views: [UIView]) -> NSData? {

    if views.isEmpty {
        return nil
    }

    let pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, CGRect(x: 0, y: 0, width: 612, height: 792), nil)

    let context = UIGraphicsGetCurrentContext()

    for view in views {
        UIGraphicsBeginPDFPage()
        view.layer.renderInContext(context)
    }

    UIGraphicsEndPDFContext()

    return pdfData
}

It is fairly simple. First I create pdf context. And then loop through views array. For each view It renders view into pdf context.

I hope this will help.

Upvotes: 9

Related Questions