Как определить, какие элементы NSCollectionViewItem действительно видны

Я использую NSCollectionView, который показывает изображения, которые мне нужно отобразить в фоновом режиме. Этот рендеринг занимает значительное время (например, от 100 мс до более секунды).

Я использую обычный макет потока (NSCollectionViewFlowLayout) с NSCollectionViewItem тайлами одинакового размера.

Как только collectionView запускается, он создает гораздо больше экземпляров элементов (плиток), чем фактически умещается в текущем представлении. Например, если в представление помещается 6 плиток, требуется около 20 плиток. Я запускаю процесс рендеринга для каждого созданного тайла. Как только изображение было обработано, плитка получает уведомление, что, в свою очередь, приводит к обновлению ее представления.

Теперь, пока идет рендеринг, пользователь может прокручивать вниз, например. до последних 6 плиток. Это создает проблему с производительностью:

В очереди еще много рендеров для тайлов, которые еще не были видны и не будут видны в ближайшее время (они были пропущены пользователем, когда он пролистал сразу до конца). CollectionView, теперь запрашивающий представления для теперь видимых последних 6 плиток, отбросит 6 из своих ранее выделенных плиток, 4 из которых, возможно, уже были отрисованы и помечены как отбрасываемые в любом случае, поэтому только 2 из оставшихся 16 плиток в очереди рендеринга будут уведомлены о том, что они не понадобятся и будут удалены из очереди. И новые видимые 6 плиток с конца будут добавлены в очередь рендеринга.

Это означает, что до того, как пользователь начнет видеть какие-либо изображения из того места, до которого он прокручивал, необходимо отобразить еще 14 изображений, даже если они не отображаются в области содержимого текущего представления коллекции.

Как я могу оптимизировать это, чтобы при прокрутке пользователем я сразу начинал отображать самые новые видимые NSCollectionViewItem? Ни одна из функций делегата, похоже, не помогает в этом. Даже такие функции, как visibleItems, возвращают не только видимые, но и все, выделенные collectionView, включая 14 невидимых.

Я мог бы при добавлении элементов в очередь всегда вставлять их в начало, а не добавлять в конец очереди. Это тоже не помогло бы, потому что тогда, изначально, когда collectionView запрашивает 20 плиток, я в конечном итоге сначала отрисовывал бы 20-ю плитку и, таким образом, не отображал бы никаких визуализаций до тех пор, пока не будут отрисованы 14 невидимых.


person Thomas Tempelmann    schedule 30.03.2019    source источник


Ответы (1)


Документация для -[NSCollectionView visibleItems]:

Этот массив может содержать элементы, которые находятся за пределами фактического видимого прямоугольника представления коллекции. Например, он может содержать элементы, которые недавно были видны, но с тех пор исчезли из поля зрения. Чтобы проверить, виден ли элемент на самом деле, проверьте, пересекает ли его прямоугольник кадра visibleRect представления коллекции.

Итак, вот функция, которая выполняет этот тест, возвращая только действительно видимые элементы:

- (NSArray<NSCollectionViewItem*> * _Nonnull) trulyVisibleItemsInCollectionView:(NSCollectionView*)collectionView
{
    NSMutableArray<NSCollectionViewItem*> *result = [NSMutableArray array];
    NSArray<NSCollectionViewItem *> *items = collectionView.visibleItems;
    NSRect viewRect = collectionView.visibleRect;
    for (NSCollectionViewItem *item in items) {
        if (NSIntersectsRect(item.view.frame, viewRect)) {
            [result addObject:item];
        }
    }
    return result;
}

Однако это по-прежнему затрудняет эффективное управление очередью рендеринга:

  1. Когда мой collectionView:itemForRepresentedObjectAtIndexPath: вызывается, представления еще не добавляются в collectionView, поэтому слишком рано определять, следует ли планировать немедленный рендеринг этой плитки.

  2. Таймер можно использовать для периодической проверки видимых элементов и перемещения их в начало очереди. Его не следует использовать для принятия решения о вставке элементов в очередь, потому что, если таймер срабатывает слишком рано после создания объекта плитки, он еще не будет добавлен в представление, что приведет к большей задержке до начинается рендеринг. Поэтому я считаю, что лучше всего добавлять элементы в модуль визуализации, как только он узнает, что они могут понадобиться, но переупорядочивать очередь, как только станет ясно, какие элементы будут видны, а какие нет.

person Thomas Tempelmann    schedule 30.03.2019