UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Export to PDF, multi page PDF

Forums > SwiftUI

@Bnerd  

I am using Paul's method to extract a custom View to PDF. https://www.hackingwithswift.com/quick-start/swiftui/how-to-render-a-swiftui-view-to-a-pdf

My custom View is dynamic (I am using a LazyVGrid) and although the width is fixed to (let's say to A4 page size) the height grows as per the content. Does anyone know how to add pages into pdf as per the View height?

Is something that PDFKit could address? I am trying to find a guide on how to use but I am getting confused. So far I mastered Paul's method but the limitation is when the View exceeds the A4 size height...

Thanks!

3      

Can a simple option add a fixed height to a fixed width by simply setting the parameter for the A4 format: height = width * √2. Or does LazyVGrid not support fixed height?

3      

@Bnerd  

Hi Sergey, the LazyVGrid grows as the contents needs, thus limiting the height it won't help. Using Paul's method I can create a perfect A4 page pdf, but if my content is bigger the extra one is not plotted in the A4..

The challenge is somehow to tell SWIFT that when the height of the content exceeds 842(A4 height in points), to introduce more pages. Using pdf.beginPDFPage and pdf.endPDFPage I can add pages in the pdf but they are just copies of the first one.

3      

Oops, then I don’t know, I misunderstood the question and thought that you need all the content to be on one page.

3      

As I understand there is no easy way to make several pages with image renderer as it basically creates image and then places in pdf. It seems like that if you use image renderer you will need to calculate how many grid items you want per page and then put some logic in code how to calculate that.

THE CODE UPDATED!

  // Your gridview
struct GridView: View {
    var text: String

    var body: some View {
        Text(text)
            .font(.largeTitle)
            .foregroundColor(.white)
            .padding(.vertical, 20)
            .padding(.horizontal, 100)
            .background(.blue)
            .clipShape(Capsule())

    }

    static func fetchItems() -> [String] {
        var arr = [String]()
        for num in 1...100 {
            arr.append("View \(num)")
        }
        return arr
    }
}

struct ContentView: View {
    @State private var items = GridView.fetchItems()

    var body: some View {
        NavigationStack {
            // I have chosen to have 10 views per page
            ShareLink("Export PDF", item: render(viewsPerPage: 10))
            ScrollView {
                LazyVGrid(columns: [GridItem()]) {
                    ForEach(items, id: \.self) { item in
                        GridView(text: item)
                    }
                }
            }
            .navigationTitle("Grid view")
        }

    }

    @MainActor func render(viewsPerPage: Int) -> URL {
        // Save it to our documents directory
        let url = URL.documentsDirectory.appending(path: "output.pdf")

        // Tell SwiftUI our PDF should be of certain size
        var box = CGRect(x: 0, y: 0, width: 600, height: 1200)

        // Create the CGContext for our PDF pages
        guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else {
            return url
        }

        // Calculate number of pages based on passed amount of viewsPerPage
        // you would like to have
        let numberOfPages = items.count / viewsPerPage

        var index = 0
        for _ in 0..<numberOfPages {

            // Start a new PDF page
            pdf.beginPDFPage(nil)

            // Render necessary views
            for num in 0..<viewsPerPage {
                let renderer = ImageRenderer(content: GridView(text: items[index]))
                renderer.render { size, context in

                    // Will place the view in the middle of pdf on x-axis
                    let xTranslation = box.size.width / 2 - size.width / 2

                    // Spacing between the views on y-axis
                    let spacing: CGFloat = 20

                    // TODO: - View starts printing from bottom, need to inverse Y position
                    pdf.translateBy(
                        x: xTranslation - min(max(CGFloat(num) * xTranslation, 0), xTranslation),
                        y: size.height + spacing
                    )

                    // Render the SwiftUI view data onto the page
                    context(pdf)
                    // End the page and close the file
                }
                index += 1

            }
            pdf.endPDFPage()
        }
        pdf.closePDF()
        return url
        }
}

I have put sample data in the file so it is clear what is what. The only issue that views are drawn from x: 0, y: 0 from the left bottom side. Could not find the way to make it go from top to bottom instead of bottom up...

Here how it looks like when you try to print: https://ibb.co/zshHPsT

4      

I'm so glad I found this post, as I've been struggling with this too. I have rebuilt my LazyVGrid to now correctly handle 1 Grid per loop, but when I try to add the ShareLink to my View - nothing happens. What am I doing wrong? My Current code is available on Git - https://github.com/TheApApp/cardtracker/blob/main/SwiftUI/ViewEventsView.swift - Ah! I figured it out, now to just reformat the page to correctly print all the content :). Thanks again for this Forum Thread!

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.