Swift 5-如何使用PDFKit创建PDF表 [英] Swift 5 - How to create table in PDF with PDFKit

查看:107
本文介绍了Swift 5-如何使用PDFKit创建PDF表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在UIGraphicsPDFRendererFormat中具有高度为400的信息,并且想要在其中附加表格,但是如果表格大于页面,则需要自动创建新页面并在其他页面中继续使用表格.

I have information in UIGraphicsPDFRendererFormat with height 400 and I want to attach a table in it, but I need if the table is bigger than the page to create automatically new pages and continues with the table in the other pages.

我找到了有关将UITableView转换为PDF的答案,但是我不使用UITableView.

I found answers about converting UITableView to PDF, but I don't use UITableView.

我要用以下数组填充表格:名称:[String],地址:[String],金钱:[Double] 数组货币应在表末尾自动求和.

I'm going to fill the table with arrays: names:[String] , addresses:[String], money:[Double] The Array Money should be summed automatically at the end of the table.

如果内容较大,如何绘制并自动创建新页面?

So how can be drawn and automatically to create new pages, if the content is bigger?

推荐答案

要计算每页的项目数,可以使用以下公式:

For counting the number of items per page you could use the formula:

pageRectangle.height/rowHeight

,从而找出每页的元素数.您也可以选择减去顶部和底部的缩进量.它看起来应该像下一个:

and thus find out number of elements per page. Optionally you could subtract the top and bottom indents also. And it should look like the next:

您可以在下面找到示例项目,该示例项目详细解答了您的问题(将其放入创建的项目的ViewController中)

You could find the example project below, that answers your question in detail (put it into a created project's ViewController)

import UIKit
import PDFKit

// Mock data
let names = ["Jenice Littler", "Marianna Harned", "Rosendo Laubach", "Lawrence Ritchie", "Dolores Mcnelly", "Jarred Edens", "Lakia Harten", "Jannette Duer", "Johnny Stinger", "Olympia Mclane", "Manda Kersh", "Clint Muller", "Anjanette Simkins", "Genny Mayhew", "Bonny Risser", "Bridgette Groth", "Glenda Mirarchi", "Omar Ashbrook", "Deedee Luker", "Caitlyn Borgeson", "Arden Maloy", "Orval Esper", "Carole Wendland", "Tiffanie Popp", "Whitley Davie", "Janett Carrara", "Rosia Stalvey", "Taunya Pinkney", "Bertram Hemmingway", "Dalia Hulett", "Alejandrina Heinz", "Marlena Goold", "Randa Cornish", "Nicolette Drexler", "Christi Collinson", "Harris Pesina", "Natasha Sommers", "Terrance Jarboe", "Avery Prouty", "Louise Shire", "Corrie Kapinos", "Bao Milstead", "Tran Mcpeak", "Cody Dewald", "Paris Newkirk", "Felisha Verona", "Milo Eno", "Nicole Bryand", "Anya Wierenga", "Dinah Levitsky"]

let addresses = ["667 Gates St. Paterson, NJ 07501", "6 Riverside Court Ellicott City, MD 21042", "282 Philmont Street Howell, NJ 07731", "120 N. Lake View Drive Wheaton, IL 60187", "748 S. Riverview St. Chapel Hill, NC 27516", "7038 Bridle Road Pottstown, PA 19464", "84 South Peninsula Dr. Merrick, NY 11566", "65 Blackburn Drive West Islip, NY 11795", "3 Whitemarsh Street Piscataway, NJ 08854", "8559 Mayfield Lane Taylor, MI 48180", "51 St Margarets Dr. Phoenixville, PA 19460", "224 East Bridgeton Circle Billings, MT 59101", "7 Arlington Drive Boca Raton, FL 33428", "74 Jennings St. Irwin, PA 15642", "9257 Mill Pond Street Painesville, OH 44077", "447 Rockaway Ave. Des Plaines, IL 60016", "34 Princess St. Waldorf, MD 20601", "8486 West Glen Creek Ave. Amsterdam, NY 12010", "748 Summer Rd. Los Angeles, CA 90008", "31 Victoria Lane Latrobe, PA 15650", "838 Central Ave. Utica, NY 13501", "123 Vine Rd. Lawndale, CA 90260", "632 Del Monte Dr. Williamstown, NJ 08094", "8626 Country Drive Williamsport, PA 17701", "4 Vermont St. Elizabethtown, PA 17022", "7752 Laurel Lane Ambler, PA 19002", "828 Creek Dr. Biloxi, MS 39532", "7950 South Pennsylvania Ave. Hollywood, FL 33020", "7645 Tunnel Ave. Culpeper, VA 22701", "199 Hudson St. Kaukauna, WI 54130", "689 Berkshire St. Apple Valley, CA 92307", "97A Lyme Ave. Janesville, WI 53546", "134 Cedarwood St. Middleton, WI 53562", "738 Coffee Street Elizabethton, TN 37643", "569 College Drive Buffalo, NY 14215", "87 Front Street State College, PA 16801", "8069 Kirkland Drive Portage, IN 46368", "48 Eagle Ave. Elmont, NY 11003", "833 Virginia Ave. Encino, CA 91316", "662 Boston Court Staten Island, NY 10301", "7233 Devonshire Court New Brunswick, NJ 08901", "65 Virginia Drive Seattle, WA 98144", "388 Sierra St. Middleburg, FL 32068", "641 SW. Gulf St. Waterbury, CT 06705", "8279 Mill Pond St. Staunton, VA 24401", "56 Deerfield St. Cranberry Twp, PA 16066", "9289 Hilldale Ave. Lakeland, FL 33801", "7486 Poplar St. Brainerd, MN 56401", "2 Berkshire Road Missoula, MT 59801", "160 Kirkland Ave. Clifton Park, NY 12065"]

let money = [55.08, 87.75, 72.00, 96.79, 17.07, 85.36, 75.44, 70.35, 28.39, 46.17, 84.70, 37.47, 23.22, 97.98, 56.23, 47.89, 40.56, 86.50, 30.93, 50.15, 22.19, 95.70, 26.48, 79.48, 15.27, 15.00, 45.36, 88.26, 11.49, 48.96, 58.58, 56.04, 95.31, 21.56, 63.44, 90.83, 22.12, 88.17, 19.75, 30.65, 47.00, 39.82, 26.83, 91.12, 54.61, 35.06, 16.54, 78.64, 90.66, 41.81]

struct TableDataItem {
    let name: String
    let address: String
    let money: Double

    init(name: String, address: String, money: Double) {
        self.name = name
        self.address = address
        self.money = money
    }
}

class ViewController: UIViewController {
    var pdfView: PDFView!

    override func viewDidLoad() {
        super.viewDidLoad()
        createUI()
        createPDF()
    }

    func createUI() {
        pdfView = PDFView()
        pdfView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(pdfView)
        pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        pdfView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        pdfView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }

    func createPDF() {
        var tableDataItems = [TableDataItem]()
        for itemIndex in 0..<names.count {
            tableDataItems.append(TableDataItem(name: names[itemIndex], address: addresses[itemIndex], money: money[itemIndex]))
        }
        let sumItem = money.reduce(0, +)
        tableDataItems.append(TableDataItem(name: "", address: "sum:", money: sumItem))
        let tableDataHeaderTitles =  ["name", "address", "money"]
        let pdfCreator = PDFCreator(tableDataItems: tableDataItems, tableDataHeaderTitles: tableDataHeaderTitles)

        let data = pdfCreator.create()
        pdfView.document = PDFDocument(data: data)
        pdfView.autoScales = true
    }

}

// inspired by Paul Hudson
extension Array {
    func chunkedElements(into size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map {
            Array(self[$0 ..< Swift.min($0 + size, count)])
        }
    }
}

class PDFCreator: NSObject {
    let defaultOffset: CGFloat = 20
    let tableDataHeaderTitles: [String]
    let tableDataItems: [TableDataItem]

    init(tableDataItems: [TableDataItem], tableDataHeaderTitles: [String]) {
        self.tableDataItems = tableDataItems
        self.tableDataHeaderTitles = tableDataHeaderTitles
    }

    func create() -> Data {
        // default page format
        let pageWidth = 8.5 * 72.0
        let pageHeight = 11 * 72.0
        let pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
        let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: UIGraphicsPDFRendererFormat())

        let numberOfElementsPerPage = calculateNumberOfElementsPerPage(with: pageRect)
        let tableDataChunked: [[TableDataItem]] = tableDataItems.chunkedElements(into: numberOfElementsPerPage)

        let data = renderer.pdfData { context in
            for tableDataChunk in tableDataChunked {
                context.beginPage()
                let cgContext = context.cgContext
                drawTableHeaderRect(drawContext: cgContext, pageRect: pageRect)
                drawTableHeaderTitles(titles: tableDataHeaderTitles, drawContext: cgContext, pageRect: pageRect)
                drawTableContentInnerBordersAndText(drawContext: cgContext, pageRect: pageRect, tableDataItems: tableDataChunk)
            }
        }
        return data
    }

    func calculateNumberOfElementsPerPage(with pageRect: CGRect) -> Int {
        let rowHeight = (defaultOffset * 3)
        let number = Int((pageRect.height - rowHeight) / rowHeight)
        return number
    }
}

// Drawings
extension PDFCreator {
    func drawTableHeaderRect(drawContext: CGContext, pageRect: CGRect) {
        drawContext.saveGState()
        drawContext.setLineWidth(3.0)

        // Draw header's 1 top horizontal line
        drawContext.move(to: CGPoint(x: defaultOffset, y: defaultOffset))
        drawContext.addLine(to: CGPoint(x: pageRect.width - defaultOffset, y: defaultOffset))
        drawContext.strokePath()

        // Draw header's 1 bottom horizontal line
        drawContext.move(to: CGPoint(x: defaultOffset, y: defaultOffset * 3))
        drawContext.addLine(to: CGPoint(x: pageRect.width - defaultOffset, y: defaultOffset * 3))
        drawContext.strokePath()

        // Draw header's 3 vertical lines
        drawContext.setLineWidth(2.0)
        drawContext.saveGState()
        let tabWidth = (pageRect.width - defaultOffset * 2) / CGFloat(3)
        for verticalLineIndex in 0..<4 {
            let tabX = CGFloat(verticalLineIndex) * tabWidth
            drawContext.move(to: CGPoint(x: tabX + defaultOffset, y: defaultOffset))
            drawContext.addLine(to: CGPoint(x: tabX + defaultOffset, y: defaultOffset * 3))
            drawContext.strokePath()
        }

        drawContext.restoreGState()
    }

    func drawTableHeaderTitles(titles: [String], drawContext: CGContext, pageRect: CGRect) {
        // prepare title attributes
        let textFont = UIFont.systemFont(ofSize: 16.0, weight: .medium)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = .center
        paragraphStyle.lineBreakMode = .byWordWrapping
        let titleAttributes = [
            NSAttributedString.Key.paragraphStyle: paragraphStyle,
            NSAttributedString.Key.font: textFont
        ]

        // draw titles
        let tabWidth = (pageRect.width - defaultOffset * 2) / CGFloat(3)
        for titleIndex in 0..<titles.count {
            let attributedTitle = NSAttributedString(string: titles[titleIndex].capitalized, attributes: titleAttributes)
            let tabX = CGFloat(titleIndex) * tabWidth
            let textRect = CGRect(x: tabX + defaultOffset,
                                  y: defaultOffset * 3 / 2,
                                  width: tabWidth,
                                  height: defaultOffset * 2)
            attributedTitle.draw(in: textRect)
        }
    }

    func drawTableContentInnerBordersAndText(drawContext: CGContext, pageRect: CGRect, tableDataItems: [TableDataItem]) {
        drawContext.setLineWidth(1.0)
        drawContext.saveGState()

        let defaultStartY = defaultOffset * 3

        for elementIndex in 0..<tableDataItems.count {
            let yPosition = CGFloat(elementIndex) * defaultStartY + defaultStartY

            // Draw content's elements texts
            let textFont = UIFont.systemFont(ofSize: 13.0, weight: .regular)
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = .center
            paragraphStyle.lineBreakMode = .byWordWrapping
            let textAttributes = [
                NSAttributedString.Key.paragraphStyle: paragraphStyle,
                NSAttributedString.Key.font: textFont
            ]
            let tabWidth = (pageRect.width - defaultOffset * 2) / CGFloat(3)
            for titleIndex in 0..<3 {
                var attributedText = NSAttributedString(string: "", attributes: textAttributes)
                switch titleIndex {
                case 0: attributedText = NSAttributedString(string: tableDataItems[elementIndex].name, attributes: textAttributes)
                case 1: attributedText = NSAttributedString(string: tableDataItems[elementIndex].address, attributes: textAttributes)
                case 2: attributedText = NSAttributedString(string: String(format: "%.2f", tableDataItems[elementIndex].money), attributes: textAttributes)
                default:
                    break
                }
                let tabX = CGFloat(titleIndex) * tabWidth
                let textRect = CGRect(x: tabX + defaultOffset,
                                      y: yPosition + defaultOffset,
                                      width: tabWidth,
                                      height: defaultOffset * 3)
                attributedText.draw(in: textRect)
            }

            // Draw content's 3 vertical lines
            for verticalLineIndex in 0..<4 {
                let tabX = CGFloat(verticalLineIndex) * tabWidth
                drawContext.move(to: CGPoint(x: tabX + defaultOffset, y: yPosition))
                drawContext.addLine(to: CGPoint(x: tabX + defaultOffset, y: yPosition + defaultStartY))
                drawContext.strokePath()
            }

            // Draw content's element bottom horizontal line
            drawContext.move(to: CGPoint(x: defaultOffset, y: yPosition + defaultStartY))
            drawContext.addLine(to: CGPoint(x: pageRect.width - defaultOffset, y: yPosition + defaultStartY))
            drawContext.strokePath()
        }
        drawContext.restoreGState()
    }
}

用过的材料:

这篇关于Swift 5-如何使用PDFKit创建PDF表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆