CATiledLayer показывает предыдущие плитки
При аннулировании представления, поддерживаемого CATiledLayer, предыдущий тайл остается "застрявшим" и неправильно аннулируется.
Похоже, это происходит, когда представление недействительно (в основном потоке), в то время как потоки рендеринга плитки все еще работают с предыдущей версией плитки. Вместо того, чтобы кэшировать новую версию плитки, предыдущая версия кэшируется.
Представление, поддерживаемое CATiledLayer, является подпредставлением UIScrollView и может масштабироваться. Рендеринг тайла может быть дорогим и может использовать нить рендеринга в течение 10 мс.
пример
Пример кода, демонстрирующий эту проблему: https://github.com/Q42/CATiledLayerBug
- В CATiledLayer, начните рендеринг всех красных плиток (это займет около 3 секунд)
- Каждый шаг рендеринга занимает около 10 мс
- Во время рендеринга (после 800 мс) аннулируйте полное представление:
tiledView.setNeedsDisplay()
- Начало рендеринга всех серых плиток (это снова занимает около 3 секунд)
- Две плитки (случайно?) Остаются красными, а не серыми.
Увидеть 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)
}
}
}