Skip to content

Instantly share code, notes, and snippets.

@otmb
Last active September 21, 2023 14:54
Show Gist options
  • Save otmb/deedb649829b2aa188a1032a9bc69522 to your computer and use it in GitHub Desktop.
Save otmb/deedb649829b2aa188a1032a9bc69522 to your computer and use it in GitHub Desktop.
Create multi-page PDFs async using SwiftUI layouts
import SwiftUI
import PDFKit
let pdfRendererFormat = [
kCGPDFContextCreator: "@mbotsu",
kCGPDFContextAuthor: "@mbotsu",
kCGPDFContextTitle: "Create multi-page PDFs with SwiftUI layouts",
kCGPDFContextSubject: "Give up ImageRenderer and create PDFs with UIGraphicsPDFRenderer",
] as [String : Any]
struct CreatePDFView: View {
@State var pdfUrl: URL?
var body: some View {
VStack {
if let pdfUrl = pdfUrl {
PdfFileView(url: pdfUrl)
}
}
.onAppear {
let views: [AnyView] = [
AnyView(Page1()),
AnyView(Page2()),
AnyView(Page3()),
]
Task {
do {
let url = try await createPdf("sample.pdf",
options: pdfRendererFormat,
views: views)
pdfUrl = url
print(url.path)
} catch {
print(error.localizedDescription)
}
}
}
}
struct Page1: View {
var body: some View {
Text("Page1").font(.largeTitle)
}
}
struct Page2: View {
var body: some View {
Text("Page2").font(.largeTitle)
}
}
struct Page3: View {
var body: some View {
Text("Page3").font(.largeTitle)
}
}
}
func createUrl(fileName: String) throws -> URL {
let fileManager = FileManager.default
let url = fileManager.temporaryDirectory.appendingPathComponent(fileName, conformingTo: .pdf)
if fileManager.fileExists(atPath: url.path) {
try fileManager.removeItem(at: url)
}
return url
}
func createPdf(_ fileName: String, options: [String: Any],
width:CGFloat=595.2, height:CGFloat=841.8,
views: [AnyView]) throws -> URL {
let format = UIGraphicsPDFRendererFormat()
format.documentInfo = options
let url = try createUrl(fileName: fileName)
let pdfRect = CGRect(x: 0, y: 0, width: width, height: height)
let pdfRenderer = UIGraphicsPDFRenderer(bounds: pdfRect, format: format)
try pdfRenderer.writePDF(to: url , withActions: { context in
views.forEach{ view in
_createPdf(view, rect: pdfRect, context: context)
}
})
return url
}
func _createPdf(_ view: AnyView, rect: CGRect,
context: UIGraphicsPDFRendererContext){
let vc = UIHostingController(rootView: view)
vc.view.frame = rect
vc.loadView()
context.beginPage()
context.cgContext.clear(rect)
vc.view.drawHierarchy(in: rect, afterScreenUpdates: true)
vc.removeFromParent()
vc.view.removeFromSuperview()
}
struct PdfFileView : UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> some PDFView {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: url)
pdfView.autoScales = true
pdfView.displayMode = .twoUpContinuous
return pdfView
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment