Генерация 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