Генерация PDF отчётов на Swift: PDFKit и UIPrintPageRenderer

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Генерация PDF отчётов на Swift (iOS/macOS) с помощью PDFKit

Создание PDF-документов — одна из частых задач в мобильных и десктопных приложениях. На платформах Apple (iOS, macOS, watchOS, tvOS) для этого используется встроенный фреймворк PDFKit. Он позволяет программно генерировать, отображать и редактировать PDF-файлы. В этой статье мы разберём, как с помощью Swift создать полноценный PDF-отчёт: с текстом, таблицами, изображениями и настраиваемыми страницами.

Что такое PDFKit?

PDFKit — это фреймворк от Apple, который входит в стандартные SDK iOS/macOS. Он предоставляет классы для работы с PDF:

  • PDFDocument — представление всего документа.
  • PDFPage — отдельная страница.
  • PDFView — отображение PDF на экране.
  • UIPrintPageRenderer (для iOS) — генерация страниц через рисование Core Graphics.

Для генерации отчётов чаще всего используют UIPrintPageRenderer или рисуют напрямую через Core Graphics в контексте PDF. Второй способ даёт больше гибкости, но требует больше кода.

Подготовка проекта

Откройте Xcode, создайте новый проект для iOS или macOS. Убедитесь, что импортировали модуль PDFKit:

import PDFKit
import UIKit // для iOS

Для macOS потребуется import Cocoa и соответствующая работа с NSView.

Способ 1: Генерация PDF через UIPrintPageRenderer (iOS)

Этот метод удобен, если вы хотите использовать HTML-разметку или сложные макеты. UIPrintPageRenderer позволяет рендерить страницы через UIPrintFormatter.

func generatePDFUsingRenderer() -> Data? {
    let renderer = UIPrintPageRenderer()
    
    // Настройка размера страницы A4
    let pageSize = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // в пунктах
    renderer.setValue(pageSize, forKey: "paperRect")
    renderer.setValue(pageSize, forKey: "printableRect")
    
    // Создаём HTML-строку с отчётом
    let htmlString = """
    <h1>Отчёт по продажам</h1>
    <p>Дата: 2025-03-17</p>
    <table border="1">
        <tr><th>Товар</th><th>Количество</th><th>Цена</th></tr>
        <tr><td>Ноутбук</td><td>5</td><td>50000</td></tr>
        <tr><td>Мышь</td><td>20</td><td>1500</td></tr>
    </table>
    """
    
    let formatter = UIMarkupTextPrintFormatter(markupText: htmlString)
    renderer.addPrintFormatter(formatter, startingAtPageAt: 0)
    
    // Рендеринг в PDF
    let pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)
    
    for pageIndex in 0..<renderer.numberOfPages {
        UIGraphicsBeginPDFPage()
        renderer.drawPage(at: pageIndex, in: UIGraphicsGetCurrentContext()!)
    }
    
    UIGraphicsEndPDFContext()
    return pdfData as Data
}

Вызовите функцию и сохраните результат:

if let pdfData = generatePDFUsingRenderer() {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let fileURL = paths[0].appendingPathComponent("report.pdf")
    try? pdfData.write(to: fileURL)
    print("PDF сохранён: \(fileURL)")
}

Способ 2: Рисование через Core Graphics (iOS/macOS)

Этот метод более низкоуровневый, но даёт полный контроль над каждым элементом. Подходит для macOS, где UIPrintPageRenderer недоступен.

func generatePDFWithCoreGraphics() -> Data? {
    let pdfData = NSMutableData()
    let pageRect = CGRect(x: 0, y: 0, width: 612, height: 792) // US Letter
    
    guard let consumer = CGDataConsumer(data: pdfData),
          let pdfContext = CGContext(consumer: consumer, mediaBox: nil, nil) else {
        return nil
    }
    
    // Начинаем страницу
    pdfContext.beginPDFPage(nil)
    
    // Рисуем текст
    let title = "Годовой отчёт"
    let attributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.boldSystemFont(ofSize: 24),
        .foreground                    

Рекомендаций