Search code examples
iospdfswiftui

Convert SwiftUI View to PDF on iOS


I was drawing some nice graphs with SwiftUI, because it is so simple and easy to do. Then I wanted to export the whole SwiftUI View to a PDF such that someone else can view the graphs in a nice way. SwiftUI does not offer a solution for this directly.

Cheers,
Alex


Solution

  • After some thinking I came up with the idea of combining the UIKit to PDF method and SwiftUI.

    At first you create your SwiftUI view, then you put into an UIHostingController. You render the HostingController on a window behind all other views and and draw its layer on a PDF. Sample code is listed below.

    func exportToPDF() {
    
        let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let outputFileURL = documentDirectory.appendingPathComponent("SwiftUI.pdf")
    
        //Normal with
        let width: CGFloat = 8.5 * 72.0
        //Estimate the height of your view
        let height: CGFloat = 1000
        let charts = ChartsView()
    
        let pdfVC = UIHostingController(rootView: charts)
        pdfVC.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
    
        //Render the view behind all other views
        let rootVC = UIApplication.shared.windows.first?.rootViewController
        rootVC?.addChild(pdfVC)
        rootVC?.view.insertSubview(pdfVC.view, at: 0)
    
        //Render the PDF
        let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: 8.5 * 72.0, height: height))
    
        do {
            try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in
                context.beginPage()
                pdfVC.view.layer.render(in: context.cgContext)
            })
    
            self.exportURL = outputFileURL
            self.showExportSheet = true
    
        }catch {
            self.showError = true
            print("Could not create PDF file: \(error)")
        }
    
        pdfVC.removeFromParent()
        pdfVC.view.removeFromSuperview()
    }