如何在后台线程上呈现 CALayer 的内容

How to Render Content of CALayer on a Background Thread

提问人:Brew Master 提问时间:5/15/2023 最后编辑:Brew Master 更新时间:5/25/2023 访问量:436

问:

我正在实现一个需要在后台线程上执行绘图的 CALayer。但是,尽管将属性“drawsAsynchronously”设置为 true,但仍在主线程上调用 draw 函数。这是我目前的实现:

class DKVectorLayer : CALayer {
    private let renderQueue = DispatchQueue( label: "Render queue, qos: .userInitiated)

    override func draw(in ctx: CGContext) {
        // This is still running in the main thread
    }
}

我尝试使用“renderQueue.async”将渲染代码移动到后台队列,但它似乎没有按预期工作。在“renderQueue.async”闭包中执行的绘图代码似乎被忽略,并且绘制发生在“draw(in:)”函数之后。

override func draw(in ctx: CGContext) {
    renderQueue.async {
        // Render code here, but it does not display on the screen
    }
}

有没有办法在后台线程上呈现 CALayer 的内容?我知道 CATiledLayer 可以在后台线程上呈现内容,但它有一些限制。例如,在调用“setNeedsDisplay(in: rect)”时,将忽略指定的矩形,并重新绘制所有图块。

Swift Core-动画 Calayer

评论

1赞 DonMag 5/21/2023
“......需要在主线程上执行绘图的 CALayer“有点模棱两可。您可以在后台线程上使用...如果您提供您正在尝试做的事情的最小实际示例,我们可以提供帮助。UIGraphicsImageRenderer
0赞 Brew Master 5/23/2023
对不起,这是我的错,我的意思是“我正在实现一个需要在后台线程上执行绘图的 CALayer”
0赞 DonMag 5/23/2023
好的 - 仍然不清楚你所说的“需要执行绘图”是什么意思......您能提供一个简单的 CALayer 绘图示例吗?
0赞 Brew Master 5/23/2023
当然,我将实现一个示例,然后在此处发布链接,谢谢!我的意思是,CALayer 的绘制代码应该在后台线程中执行,我目前为此使用 draw(in ctx),但因为我想在图层上绘制太多 UIBezierPaths,它可能会在渲染过程中阻塞主线程一秒钟。我想将其移动到后台线程以使应用程序响应速度更快
0赞 Brew Master 5/23/2023
我所说的“执行绘图”是指渲染,就像我们使用 drawRect() 在 UIVIew 上渲染内容一样,但使用 CALayer

答:

3赞 Rob Napier 5/25/2023 #1

完成后,必须完全绘制图层。不能通过在 内部调度来延迟绘制。但是,CALayer 是线程安全的,您可以自由地调用另一个线程并自行管理图层绘制。结果将被绘制到您传递的 CGContext 中。您还可以创建一个 CGImage 并将其分配给属性,只要这不是图层支持的视图。draw(in:)draw(in:)draw(in:)content

drawsAsynchronously不确定在何处执行。它确定最终呈现发生的位置(将 CGContext 指令转换为位图)。 不生成位图。它只是创建指令。draw(in:)draw(in:)

如果图层作为视图层次结构的一部分进行渲染,则它将在系统选择的线程上发生(这通常是主线程,但在动画期间可能不是主线程)。通常,你要做的是在后台线程上计算所有昂贵的内容并缓存它,所以当 UIKit 或 AppKit 要求绘制图层时,它的速度会很快。

评论

0赞 Brew Master 5/25/2023
您提供的有关 drawsAsynchronously 属性的解释是准确的:它不控制 draw(in:) 方法的执行位置。一种可能的解决方案是将图层的内容呈现为后台线程上的图像,然后在主线程上设置 CALayer 的内容。但是,这种方法有一个缺点:它需要渲染 CALayer 的全部内容,因为没有一种已知的方法可以有选择地渲染和仅替换旧图像的一部分。另一方面,draw(in:) 方法只需要渲染脏矩形内的内容
0赞 Rob Napier 5/25/2023
通常,更好的解决方案是使用多个 CALayer,而不是尝试部分更新一个。CALayers 被设计为组合(这是它们最引人注目的功能之一)。但是你绝对可以通过操作 CGImage 位图来更新部分内容(无论如何,系统都会对你的绘图命令执行此操作;如果您有特殊需求,您可以直接管理它)。但是你是对的,如果可以的话,避免直接操纵内容是很好的。它带来了许多令人头疼的问题,这些头痛是可以避免的。