Как получить быстрый UICollectionView с большим количеством изображений (50-200)?
м используюUICollectionView
в приложении, которое отображает довольно много фотографий (50-200), и яУ меня возникли проблемы с его быстрым подключением (например, с приложением Photoshop).
У меня есть обычайUICollectionViewCell
сUIImageView
как это'подвид. Я'м загрузки изображений из файловой системы, сUIImage.imageWithContentsOfFile:
в UIImageViews внутри ячеек.
Мы уже пробовали несколько подходов, но они либо содержали ошибки, либо имели проблемы с производительностью.
ПРИМЕЧАНИЕ: яя использую 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
Это выглядело многообещающе, но есть ошибка, которую я могуне выслеживать. При начальной загрузке все изображения являются одним и тем же изображением, и при прокрутке целых блоков загружается другое изображение, но все они имеют это изображение. Я'Я пытался отладить его, но я не могуЯ не могу понять это.
мы также пытались заменить NSOperationDispatch::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
Это оказалось немного медленным и нервным в первой полной прокрутке, но все хорошо после этого.
Резюме
Так что, если кто-нибудь может указать мне правильное направление, яБуду очень благодарен. Как приложение «Фото» делает этотак хорошо?
Примечание для кого-либо еще: [Принятый] ответ решил мою проблему с изображениями, появляющимися в неправильных ячейках, но я все еще получал прерывистую прокрутку даже после изменения размеров моих изображений до правильных размеров. Разобрав мой код до самого необходимого, я в конце концов обнаружил, что виновником является UIImage # imageWithContentsOfFile. Несмотря на то, что я предварительно нагревал кэш UIImages с помощью этого метода, UIImage, похоже, выполняет внутреннее ленивое кэширование. После обновления моего кода разогрева кеша используйте решение, описанное здесь -stackoverflow.com/a/10664707/71810 - У меня наконец-то была супер плавная прокрутка ~ 60FPS.