Я использую UICollectionView
в приложении, которое отображает довольно много фотографий (50-200), и у меня возникают проблемы с его мгновенным (например, как приложение с фотографией).
У меня есть пользовательский UICollectionViewCell
с UIImageView
по мере его просмотра. Я загружаю изображения из файловой системы UIImage.imageWithContentsOfFile:
в UIImageView внутри ячеек.
Я уже пробовал несколько подходов, но они либо были ошибочными, либо имели проблемы с производительностью.
ПРИМЕЧАНИЕ. Я использую RubyMotion, поэтому я напишу фрагменты кода в стиле Ruby.
Прежде всего, здесь мой пользовательский класс UICollectionViewCell для справки...
class CustomCell < UICollectionViewCell
def initWithFrame(rect)
super
@image_view = UIImageView.alloc.initWithFrame(self.bounds).tap do |iv|
iv.contentMode = UIViewContentModeScaleAspectFill
iv.clipsToBounds = true
iv.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth
self.contentView.addSubview(iv)
end
self
end
def prepareForReuse
super
@image_view.image = nil
end
def image=(image)
@image_view.image = image
end
end
Подход № 1
Сохранение простых вещей.
def collectionView(collection_view, cellForItemAtIndexPath: index_path)
cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
image_path = @image_paths[index_path.row]
cell.image = UIImage.imageWithContentsOfFile(image_path)
end
Прокрутка вверх/вниз ужасно, используя это. Это нерешительно и медленно.
Подход # 2
Добавьте немного кеширования через NSCache...
def viewDidLoad
...
@images_cache = NSCache.alloc.init
...
end
def collectionView(collection_view, cellForItemAtIndexPath: index_path)
cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
image_path = @image_paths[index_path.row]
if cached_image = @images_cache.objectForKey(image_path)
cell.image = cached_image
else
image = UIImage.imageWithContentsOfFile(image_path)
@images_cache.setObject(image, forKey: image_path)
cell.image = image
end
end
Опять же, прыгаем и медленно на первом полном свитке, но с этого момента он плавно плавает.
Подход № 3
Загрузите изображения асинхронно... (и сохраните кеширование)
def viewDidLoad
...
@images_cache = NSCache.alloc.init
@image_loading_queue = NSOperationQueue.alloc.init
@image_loading_queue.maxConcurrentOperationCount = 3
...
end
def collectionView(collection_view, cellForItemAtIndexPath: index_path)
cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
image_path = @image_paths[index_path.row]
if cached_image = @images_cache.objectForKey(image_path)
cell.image = cached_image
else
@operation = NSBlockOperation.blockOperationWithBlock lambda {
@image = UIImage.imageWithContentsOfFile(image_path)
Dispatch::Queue.main.async do
return unless collectionView.indexPathsForVisibleItems.containsObject(index_path)
@images_cache.setObject(@image, forKey: image_path)
cell = collectionView.cellForItemAtIndexPath(index_path)
cell.image = @image
end
}
@image_loading_queue.addOperation(@operation)
end
end
Это выглядело многообещающим, но есть ошибка, которую я не могу отследить. На начальном представлении загрузка всех изображений - это одно и то же изображение, и когда вы просматриваете всю загрузку блоков с другим изображением, но все имеют это изображение. Я попытался отладить его, но я не могу понять.
Я также попытался заменить NSOperation на Dispatch::Queue.concurrent.async { ... }
, но это похоже на то же самое.
Я думаю, что это, вероятно, правильный подход, если я могу заставить его работать правильно.
Подход № 4
В отчаянии я решил просто предварительно загрузить все изображения как UIImages...
def viewDidLoad
...
@preloaded_images = @image_paths.map do |path|
UIImage.imageWithContentsOfFile(path)
end
...
end
def collectionView(collection_view, cellForItemAtIndexPath: index_path)
cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
cell.image = @preloaded_images[index_path.row]
end
Это оказалось немного медленным и нерешительным в первом полном прокрутке, но все хорошо после этого.
Резюме
Итак, если кто-то может указать мне в правильном направлении, я был бы очень благодарен. Как приложение Photos делает это настолько хорошо?
Примечание для кого-либо еще: [Принятый] ответ разрешил мою проблему с изображениями, появляющимися в неправильных ячейках, но я все еще получал прерывистую прокрутку даже после изменения размера моих изображений для корректировки размеров. После того, как я снял свой код с самого начала, я в конечном итоге обнаружил, что виновником является UIImage # imageWithContentsOfFile. Несмотря на то, что я использовал pre-warming кеш UIImages, используя этот метод, UIImage, похоже, делает какое-то ленивое кэширование внутри. После обновления кода кэширования кеша, чтобы использовать решение, подробно описанное здесь, qaru.site/info/441990/... - У меня наконец была суперглавная прокрутка ~ 60FPS.