CATiledLayer показывает предыдущие плитки

При аннулировании представления, поддерживаемого CATiledLayer, предыдущий тайл остается "застрявшим" и неправильно аннулируется.

Похоже, это происходит, когда представление недействительно (в основном потоке), в то время как потоки рендеринга плитки все еще работают с предыдущей версией плитки. Вместо того, чтобы кэшировать новую версию плитки, предыдущая версия кэшируется.

Представление, поддерживаемое CATiledLayer, является подпредставлением UIScrollView и может масштабироваться. Рендеринг тайла может быть дорогим и может использовать нить рендеринга в течение 10 мс.

пример

Пример кода, демонстрирующий эту проблему: 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

Скриншот

Обходной путь?

Это кажется ошибкой в ​​реализации CATiledLayer, Поскольку я не могу это исправить, кто-нибудь знает хороший обходной путь для этой проблемы?

Я подал радар для этого: http://www.openradar.me/28648050

1 ответ

Решение

Основываясь на дальнейшей регистрации, которую я добавил в пример проекта, я думаю, что проблема заключается в следующем:

CATiledLayer имеет два потока рендеринга, которые рисуют каждую плитку. Если во время исполнения draw(_: CGRect) позвонить setNeedsDisplay называется, текущее исполнение draw вызов завершен и результат кэшируется. Кэшированное значение основано на предыдущем "источнике данных" (в данном примере это просто цвет плитки), а не на обновленном источнике данных.

Инженер службы поддержки Apple предоставил мне обходной путь:

  • Добавить updateID поле для TiledView
  • Добавить начало draw(_: CGRect) вызов сохранить текущий updateID
  • Когда "источник данных" изменится, измените updateID
  • Добавьте конец draw(_: CGRect) позвоните, сравните сохраненное updateID с текущим.
  • Если идентификаторы отличаются, запланируйте новый 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)
    }
  }
}
Другие вопросы по тегам