I am trying to render a complex view and create and display a pdf from that rendered content.
I initiate this by pressing a button and when the task is complete I want to display a sheet with the pdf.
I want to show a progressView during this process however I am noticing that the Main UI is being blocked despite the Task / Async / Await with SwiftUI.
Below is a minimally reproduced example of what I am trying to achieve:
struct ContentView: View {
let array = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]
@State var hideProgressView = true
@State var pdfURL: URL?
var body: some View {
VStack {
if !hideProgressView {
ProgressView()
.progressViewStyle(.circular)
.padding(.bottom)
}
Button {
hideProgressView = false
Task {
await renderView()
hideProgressView = true
// show pdf
}
} label: {
Text("Generate PDF")
}
.buttonStyle(.borderedProminent)
}
}
@MainActor
func renderView() async {
let url = URL.documentsDirectory.appending(path: "output.pdf")
var box = CGRect(x: 0, y: 0, width: 200, height: 200)
guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else {
return
}
for item in array {
pdf.beginPDFPage(nil)
let renderer = ImageRenderer(content:
Text(item)
)
renderer.render { size, context in
context(pdf)
}
pdf.endPDFPage()
}
pdf.closePDF()
pdfURL = url
}
}
This does not show the delay too well but with a more complicated view that takes a while to render the blocking of the UI is significant and the progressView is never shown.
I have tried to use a global queue and dispatch to background but then I can’t draw the view without crashes. I’m wondering what is the best way to render a view to a pdf without blocking the main UI or at least being able to show a progressView while the operation is ongoing.