CATiledLayer显示之前的瓷砖

4
当使一个由CATiledLayer支持的视图无效时,先前的瓷砖仍然“卡住”,没有被正确地使无效。当在主线程上使视图无效时,似乎会发生这种情况,同时瓷砖渲染线程仍在处理先前版本的瓷砖。缓存的不是新版本的瓷砖,而是先前的版本。由CATiledLayer支持的视图是UIScrollView的子视图,并且可以进行缩放。瓷砖的渲染可能很昂贵,并且可以使用渲染线程10ms。
示例:演示此问题的示例代码:https://github.com/Q42/CATiledLayerBug
  1. 在CATiledLayer中,开始渲染所有红色瓷砖(大约需要3秒钟才能完成)
  2. 每个渲染步骤大约需要10毫秒
  3. 在渲染过程中(经过800毫秒),使整个视图无效:tiledView.setNeedsDisplay()
  4. 开始渲染所有灰色瓷砖(这再次需要大约3秒钟)
  5. 两个瓷砖(随机?)仍然保持红色,而不是变成灰色。

查看此处的 update 函数:https://github.com/Q42/CATiledLayerBug/blob/master/TiledLayerTest/ViewController.swift#L45

screenshot

有解决方法吗?

这似乎是CATiledLayer实现中的一个bug。由于我无法修复它,是否有人知道解决此问题的好方法?

我已经为此提交了一个radar:http://www.openradar.me/28648050

1个回答

2

根据我添加到示例项目中的一些日志记录,我认为问题出在这里:

CATiledLayer有两个渲染线程来绘制每个瓦片。如果在执行draw(_: CGRect)调用期间调用setNeedsDisplay,则将完成当前draw调用的执行,并缓存结果。缓存值基于先前的“数据源”(仅在此示例中为瓦片颜色),而不是更新后的数据源。

一位苹果支持工程师为我提供了一个解决方法:

  • 向TiledView添加一个updateID字段
  • draw(_: CGRect)调用的开始处保存当前的updateID
  • 当“数据源”更改时,更改updateID
  • draw(_: CGRect)调用的结束处,将保存的updateID与当前ID进行比较。
  • 如果ID不同,则安排一个新的setNeedsDisplay调用。

提取:

override func draw(_ rect: CGRect) {
  let originalID = updateID

  // all actual (slow) drawing code here...

  if originalID != updateID {

    // dispatch a redraw request, but wait a little while first
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(17)) {
      self.layer.setNeedsDisplayIn(rect)
    }
  }
}

这个解决方法对于NSView不起作用,你找到其他的替代方案了吗? - jblixr
我在过去的两年中一直在使用这个解决方法,它在iOS上仍然很好用。 - Tom Lokhorst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接