SwiftUI Task阻塞UI:异步渲染视图生成PDF并显示进度
问题分析
你的代码中renderView被标记为@MainActor,导致整个PDF生成逻辑(包括循环渲染视图)都在主线程执行。当渲染复杂视图时,主线程被长时间占用,无法更新UI,因此ProgressView始终无法正常显示。
解决方案
将耗时的PDF生成逻辑移到后台线程执行,仅在需要渲染SwiftUI视图时切换回主线程(因为SwiftUI视图渲染必须依赖主线程)。具体修改如下:
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.detached { await renderView() // 回到主线程更新UI状态 await MainActor.run { hideProgressView = true } } } label: { Text("Generate PDF") } .buttonStyle(.borderedProminent) // 展示PDF的Sheet .sheet(item: $pdfURL) { url in PDFViewer(url: url) } } } // 移除@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) // 切换回主线程渲染SwiftUI视图 await MainActor.run { let renderer = ImageRenderer(content: Text(item)) renderer.render { size, context in context(pdf) } } pdf.endPDFPage() } pdf.closePDF() // 回到主线程更新pdfURL状态 await MainActor.run { pdfURL = url } } } // PDF预览视图 struct PDFViewer: View { let url: URL var body: some View { WebView(url: url) } } // UIKit WebView包装,用于预览PDF struct WebView: UIViewRepresentable { let url: URL func makeUIView(context: Context) -> WKWebView { WKWebView() } func updateUIView(_ uiView: WKWebView, context: Context) { uiView.load(URLRequest(url: url)) } }
关键说明
Task.detached:将PDF生成任务放到后台线程执行,主线程可以正常响应用户交互、更新ProgressView。MainActor.run:SwiftUI视图渲染必须在主线程完成,通过这个方法临时切换回主线程执行视图渲染,完成后立即回到后台继续处理PDF逻辑,避免长时间阻塞主线程。- 状态更新:所有修改
@State变量的操作必须在主线程执行,因此更新hideProgressView和pdfURL时,都通过MainActor.run确保在主线程执行。
这样修改后,即使渲染复杂视图,ProgressView也能正常显示,主UI不会被阻塞,PDF生成完成后会自动弹出Sheet展示内容。
内容的提问来源于stack exchange,提问作者alionthego
相关产品推荐
相关产品推荐

