Last active
September 21, 2023 23:07
-
-
Save otmb/49ad207a0b22f2962c52c0e19c6c0241 to your computer and use it in GitHub Desktop.
Create multi-page PDFs with SwiftUI layouts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()), | |
] | |
do { | |
let url = try createPdf("sample.pdf", | |
options: pdfRendererFormat, | |
views: views) | |
pdfUrl = url | |
print(url.path) | |
} catch { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
@ViewBuilder | |
func Page1() -> some View { | |
Text("Page1").font(.largeTitle) | |
} | |
@ViewBuilder | |
func Page2() -> some View { | |
Text("Page2").font(.largeTitle) | |
} | |
@ViewBuilder | |
func Page3() -> 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.view.snapshotView(afterScreenUpdates: true) | |
context.beginPage() | |
context.cgContext.clear(rect) | |
vc.view.layer.render(in: context.cgContext) | |
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