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

iOS 17中UIView转PDF失效及卡顿问题求助

iOS 17 下 PDFView 渲染卡顿的解决方案

方案一:优化 drawHierarchy 调用逻辑

drawHierarchy(in:afterScreenUpdates:) 传入 afterScreenUpdates: true 会强制触发UI刷新,这是iOS17后卡顿的核心原因之一。改成false并确保视图已完成布局后再调用,同时将PDF写入操作移到后台线程(注意:drawHierarchy必须在主线程执行,仅将PDFRenderer的写入逻辑放后台)。

示例代码:

DispatchQueue.global().async {
    let render = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    do {
        try render.writePDF(to: path, withActions: { context in
            context.beginPage()
            // 切回主线程执行视图渲染
            DispatchQueue.main.sync {
                pdfView.drawHierarchy(in: CGRect(x: 0, y: 0, width: size.width, height: size.height), afterScreenUpdates: false)
            }
        })
        DispatchQueue.main.async {
            completion(true, path)
        }
    } catch {
        DispatchQueue.main.async {
            completion(false, nil)
        }
    }
}

方案二:直接渲染 PDFDocument(推荐)

避免渲染UIView,直接利用PDFKit的原生API从PDFDocument生成PDF,性能远高于视图渲染,且支持后台线程执行。

示例代码:

DispatchQueue.global().async {
    guard let document = pdfView.document else {
        DispatchQueue.main.async { completion(false, nil) }
        return
    }
    
    let render = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    do {
        try render.writePDF(to: path, withActions: { context in
            let pageCount = document.pageCount
            for pageIndex in 0..<pageCount {
                guard let page = document.page(at: pageIndex) else { continue }
                context.beginPage()
                let pageBounds = page.bounds(for: .mediaBox)
                let scale = min(size.width / pageBounds.width, size.height / pageBounds.height)
                context.cgContext.scaleBy(x: scale, y: scale)
                page.draw(with: .mediaBox, to: context.cgContext)
            }
        })
        DispatchQueue.main.async {
            completion(true, path)
        }
    } catch {
        DispatchQueue.main.async {
            completion(false, nil)
        }
    }
}

方案三:修复 SwiftUI ImageRenderer 渲染空内容问题

如果使用SwiftUI的ImageRenderer,需确保ScrollView的内容已完全加载并布局完成,可通过状态标记或延迟渲染给视图足够的布局时间。

示例代码:

struct ContentView: View {
    @State private var isReadyToRender = false
    let scrollContent: some View
    let path: URL
    let completion: (Bool, URL?) -> Void
    
    var body: some View {
        ScrollView {
            scrollContent
                .onAppear {
                    // 延迟确保内容布局完成
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        isReadyToRender = true
                    }
                }
        }
        .onChange(of: isReadyToRender) { _, newValue in
            guard newValue else { return }
            let renderer = ImageRenderer(content: scrollContent)
            renderer.scale = UIScreen.main.scale
            if let image = renderer.uiImage {
                saveImageAsPDF(image: image)
            }
        }
    }
    
    func saveImageAsPDF(image: UIImage) {
        DispatchQueue.global().async {
            let render = UIGraphicsPDFRenderer(bounds: CGRect(origin: .zero, size: image.size))
            do {
                try render.writePDF(to: path) { context in
                    image.draw(in: CGRect(origin: .zero, size: image.size))
                }
                DispatchQueue.main.async {
                    completion(true, path)
                }
            } catch {
                DispatchQueue.main.async {
                    completion(false, nil)
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 02:05:29