You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

导出PDF时应用冻结问题求助(子视图超出屏幕时触发)

问题:导出PDF时视图超过4个Card导致应用冻结

我参考视频教程给应用添加导出PDF功能,遇到了难以调试的冻结问题:当屏幕显示的子视图超过4个Card时,选择导出PDF选项会导致应用完全冻结。

我尝试用调试器排查,但没找到冻结原因:在exportPDF代码的do{}catch{}处设置断点,能看到有效的pdfView:

(lldb) po pdfView
<UIScrollView: 0x105823600; frame = (0 59; 430 942); clipsToBounds = YES; tag = 1009; gestureRecognizers = <NSArray: 0x2819abea0>; layer = <CALayer: 0x281700200>; contentOffset: {0, 0}; contentSize: {430, 942}; adjustedContentInset: {0, 0, 0, 0}>

但离开闭包后应用就会冻结,永远无法到达getRootController().view.subviews.forEach的断点。

以下是简化后的调用代码(已移除CoreData查询和属性定义):

struct ViewEventsView: View {
    // MARK: PDF Properties
    @State var PDFUrl: URL?
    @State var showShareSheet: Bool = false
    
    var body: some View {
        ScrollView {
            VStack {
                HStack {
                        AddressView(recipient: recipient)
                            .scaledToFit()
                            .frame(width: 250, height: 150)
                    }  
                }
                ScrollView {
                    LazyVGrid(columns: gridLayout, alignment: .center, spacing: 5) {
                        ForEach(events, id: \.self) { event in
                            HStack {
                                VStack {
                                    Image(uiImage: (event.cardFrontImage ?? blankCardFront)!)
                                        .resizable()
                                        .aspectRatio(contentMode: .fit)
                                        .scaledToFit()
                                        .frame(width: iPhone ? 120 : 200, height: iPhone ? 120 : 200)
                                        .padding(.top, iPhone ? 2: 5)
                                    HStack {
                                        VStack {
                                            Text("\(event.event ?? \"")")
                                                .foregroundColor(.green)
                                            Spacer()
                                            HStack {
                                                Text("\(event.eventDate ?? NSDate(), formatter: ViewEventsView.eventDateFormatter)")
                                                    .fixedSize()
                                                    .foregroundColor(.green)
                                                MenuOverlayView(recipient: recipient, event: event)
                                            }
                                        }
                                        .padding(iPhone ? 1 : 5)
                                        .font(iPhone ? .caption : .title3)
                                        .foregroundColor(.primary)
                                    }
                                }
                            }
                            .padding()
                            .frame(minWidth: iPhone ? 160 : 320, maxWidth: .infinity,
                                   minHeight: iPhone ? 160 : 320, maxHeight: .infinity)
                            .background(Color(UIColor.systemGroupedBackground))
                            .mask(RoundedRectangle(cornerRadius: 20))
                            .shadow(radius: 5)
                            .padding(iPhone ? 5: 10)
                        }
                        .padding()
                    }
                }
                .navigationBarTitleDisplayMode(.inline)
                .navigationBarItems(trailing:
                                        HStack {
                    Button(action: {
                        // MARK: self is Current View
                        // You can give whatever View to Conver
                        //                        print(convertToScrollView(content: {
                        //                            self
                        //                        }).contentSize)
                        exportPDF {
                            self
                        } completion: { status, url in
                            if let url = url, status {
                                PDFUrl = url
                                showShareSheet.toggle()
                            } else {
                                print("Failed to Produce PDF")
                            }
                        }
                    }, label: {
                        Image(systemName: "square.and.arrow.up.fill")
                            .font(.title2)
                            .foregroundColor(.green)
                    })
                })
            }
            .sheet(isPresented: $showShareSheet) {
                PDFUrl = nil
            } content: {
                if let PDFUrl = PDFUrl {
                    ShareSheet(urls: [PDFUrl])
                }
            }
        }
    }
}

求调试建议。


调试建议
  • 检查PDF渲染的线程归属:PDF导出的视图渲染、文件写入属于耗时操作,若在主线程执行,大量Card的内容会直接阻塞主线程导致冻结。查看exportPDF实现,确保核心逻辑放在后台队列(如DispatchQueue.global(qos: .userInitiated))执行,完成后切回主线程更新UI。

  • 排查LazyVGrid的懒加载冲突:LazyVGrid仅渲染可见区域视图,但PDF导出需要全部Card的完整渲染,未预加载的视图可能导致渲染过程中出现阻塞或死锁。临时替换为普通VGrid测试,若问题消失,说明是懒加载机制导致的,需在导出前强制触发所有视图预渲染,或改用非懒加载布局。

  • 优化图片加载逻辑:每个Card包含的图片可能在主线程解码,大量图片会占用内存并阻塞主线程。改用后台队列加载并解码图片,同时避免强制解包event.cardFrontImage ?? blankCardFront!,改用可选绑定或安全默认值,减少潜在的内存异常风险。

  • 检测线程死锁:应用冻结时,打开Xcode的Debug Navigator查看线程调用栈,重点看主线程是否存在循环等待、锁竞争情况,比如exportPDF内部是否误用了主线程同步锁,导致主线程自我阻塞。

  • 简化内容逐步排查:

    • 先导出仅含文本的Card,去掉图片和复杂布局,验证是否还会冻结
    • 逐步增加Card数量,定位触发冻结的临界点
    • 移除代码中嵌套的ScrollView,测试布局简化后的导出是否正常
  • 检查视图转换逻辑:视频教程中的exportPDF可能通过SwiftUI转UIKit视图实现渲染,复杂的视图层级(嵌套ScrollView+LazyVGrid)可能导致转换过程中出现布局计算死锁。尝试打印视图转换过程的日志,或简化导出的视图结构。


内容的提问来源于stack exchange,提问作者Michael Rowe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:40:41