首页 > 解决方案 > 如何在不阻塞主线程的情况下渲染 UIView?

问题描述

我有一个绘制很多很多UIBezierPath的视图,并且绘制它们需要很长时间(根据 Instruments 为 1.34 秒)。目前,应用程序的行为如下:我按下按钮绘制视图,应用程序冻结 1.34 秒,然后显示视图。我想在视图绘制时显示加载指示器并保持应用程序的其余部分响应。这将涉及在后台调度队列中绘制视图,但我不知道该怎么做。

MCVE:

class CustomView: UIView {
    override func draw(_ rect: CGRect) {
        // note that this takes longer to draw than my actual view
        UIColor.label.setStroke()
        for _ in 0...300 {
            let path = UIBezierPath()
            // obviously I won't be drawing random paths in the real app, but the actual shapes of the paths don't matter
            path.move(to: CGPoint(x: CGFloat.random(in: 0..<bounds.width), y: CGFloat.random(in: 0..<bounds.height)))
            for _ in 0...200 {
                path.addLine(to: CGPoint(x: CGFloat.random(in: 0..<bounds.width), y: CGFloat.random(in: 0..<bounds.height)))
            }
            path.close()
            path.stroke()
        }
    }
}
@IBAction func click() {
    let myView = CustomView(frame: CGRect(x: 10, y: 10, width: 200, height: 200))
    self.view.addSubview(myView)
}

我使用工具来尝试找出代码的哪一部分运行时间最长。令我惊讶的是,它不是双重 for 循环。它甚至不是我的代码。它似乎是一个名为的 CoreGraphics 函数aa_render

在此处输入图像描述

我试图将代码放在后台调度队列中,但正如预期的那样,它不起作用,因为您无法在后台线程中调用 UI API:

var dispatchQueue = DispatchQueue(label: "viewDrawer")
@IBAction private func click() {
    dispatchQueue.async {
        let myView = CustomView(frame: CGRect(x: 10, y: 10, width: 200, height: 200))
        self.view.addSubview(myView)
    }
}

我注意到canDrawConcurrentlymacOS 中有一个,但它似乎在 iOS 上不可用。

如何在后台绘制视图?


为什么我要画这么多路径?

我实际上是在使用 GeoJSON 绘制地图。GeoJSON 文件非常详细。文件大小高达 5MB。我实际上不需要它那么详细......我只是找不到不太详细的地图 该地图包含大约 300 个用户可以着色的区域。请注意,我很满意它需要很长时间来绘制。我只想在后台做。

读取 JSON 文件不是也需要很长时间吗?

是的,根据仪器,130ms,如果你将它与绘制视图的时间相比,这是相对较短的,这就是我关注后者的原因。

标签: iosswiftuiview

解决方案


推荐阅读