You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift PDFKit自定义PDF首页面表格起始位置实现咨询

实现方案

你需要动态调整每页表格的绘制区域,同时修正原有代码的重复绘制问题,具体修改如下:

1. 核心改动点

  • 新增变量标记当前是否为首页面,替代原有逻辑模糊的pdfHeaderStatus判断
  • 首页面表格绘制区域使用CGRect(x: 0, y: 150, width: pageWidth, height: pageHeight - 150)(高度减去偏移量避免内容溢出页面)
  • 其余页面表格绘制区域使用CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
  • 修正原有代码中表格重复绘制的问题:原代码if块内已经绘制了一次表格,块外又重复绘制了一次,会导致首页表格渲染异常
  • 针对首页和后续页面的可用高度差异,分别计算每页可容纳的表格行数,避免内容溢出

2. 修改后完整代码

class PDFCreator: NSObject {
    let defaultOffset: CGFloat = 20
    let tableDataHeaderTitles: [String]
    let tableDataItems: [TableDataItem]
    // 将原有全局变量改为类属性,避免外部全局变量依赖
    let bastarihTitle: String
    let bittarihTitle: String
    let unvanTitle: String

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

    func create() -> Data {
        let pageWidth = 8.5 * 72.0
        let pageHeight = 11 * 72.0
        // 整页渲染区域固定,仅调整表格的实际绘制区域
        let fullPageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
        let renderer = UIGraphicsPDFRenderer(bounds: fullPageRect, format: UIGraphicsPDFRendererFormat())
        
        // 分别计算首页、后续页可容纳的表格行数
        let normalPageRowCount = calculateNumberOfElementsPerPage(with: fullPageRect)
        let firstPageRowCount = calculateNumberOfElementsPerPage(with: CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight - 150))
        
        // 按行数分片处理表格数据
        var tableDataChunked: [[TableDataItem]] = []
        var remainingItems = tableDataItems
        if !remainingItems.isEmpty {
            let firstChunkCount = min(firstPageRowCount, remainingItems.count)
            tableDataChunked.append(Array(remainingItems[0..<firstChunkCount]))
            remainingItems.removeFirst(firstChunkCount)
            while !remainingItems.isEmpty {
                let chunkCount = min(normalPageRowCount, remainingItems.count)
                tableDataChunked.append(Array(remainingItems[0..<chunkCount]))
                remainingItems.removeFirst(chunkCount)
            }
        }

        let data = renderer.pdfData { context in
            var isFirstPage = true
            for tableDataChunk in tableDataChunked {
                context.beginPage()
                let tableRect: CGRect
                if isFirstPage {
                    // 首页表格从y=150位置开始绘制
                    tableRect = CGRect(x: 0, y: 150, width: pageWidth, height: pageHeight - 150)
                    
                    // 绘制首页页眉
                    let baslikTasarım = [
                        NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17),
                        NSAttributedString.Key.foregroundColor: UIColor(red: 32.0/255.0, green: 161.0/255.0, blue: 218.0/255.0, alpha: 1.0)
                    ]
                    let altBaslık = [
                        NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 11),
                        NSAttributedString.Key.foregroundColor: UIColor.black
                    ]
                    let unvanTarih = [
                        NSAttributedString.Key.font: UIFont.systemFont(ofSize: 11),
                        NSAttributedString.Key.foregroundColor: UIColor.black
                    ]
                    
                    let image = UIImage(named: "forgotpassword")
                    let che = "CARİ   HESAP   EKSTRESİ"
                    let text = "TARİH: "
                    let alici = "ALICI: "
                    let tarih = "\(bastarihTitle) - \(bittarihTitle)"
                    let unvans = "\(unvanTitle)"
                    
                    // 若原有页眉坐标显示正常,可保留原来的负数值坐标
                    che.draw(at: CGPoint(x: 30, y: -120), withAttributes: baslikTasarım)
                    alici.draw(at: CGPoint(x: 30, y: -70), withAttributes: altBaslık)
                    text.draw(at: CGPoint(x: 30, y: -20), withAttributes: altBaslık)
                    image?.draw(at: CGPoint(x: 300, y: -120))
                    tarih.draw(at: CGPoint(x: 110, y: -20), withAttributes: unvanTarih)
                    unvans.draw(at: CGPoint(x: 110, y: -70), withAttributes: unvanTarih)
                    
                    isFirstPage = false
                } else {
                    // 后续页表格从y=0位置开始绘制
                    tableRect = fullPageRect
                }
                
                // 传入动态计算的表格区域绘制表格
                let cgContext = context.cgContext
                drawTableHeaderRect(drawContext: cgContext, pageRect: tableRect)
                drawTableHeaderTitles(titles: tableDataHeaderTitles, drawContext: cgContext, pageRect: tableRect)
                drawTableContentInnerBordersAndText(drawContext: cgContext, pageRect: tableRect, tableDataItems: tableDataChunk)
            }
        }
        return data
    }

    func calculateNumberOfElementsPerPage(with pageRect: CGRect) -> Int {
        let rowHeight = (defaultOffset * 3)
        let number = Int((pageRect.height - rowHeight) / rowHeight)
        return number
    }
    
    // 以下三个表格绘制方法原有逻辑无需修改,直接接收传入的pageRect参数即可
    func drawTableHeaderRect(drawContext: CGContext, pageRect: CGRect) {
        // 原有实现保留
    }
    
    func drawTableHeaderTitles(titles: [String], drawContext: CGContext, pageRect: CGRect) {
        // 原有实现保留
    }
    
    func drawTableContentInnerBordersAndText(drawContext: CGContext, pageRect: CGRect, tableDataItems: [TableDataItem]) {
        // 原有实现保留
    }
}

内容的提问来源于stack exchange,提问作者MFEkmen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 11:15:06