我UICollectionView
在一个显示大量照片(50-200)的应用程序中使用,但我在让它变得活泼时遇到问题(例如像照片应用程序一样活泼)。
我有一个自定义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
这看起来很有希望,但有一个我无法追踪的错误。在初始视图加载时,所有图像都是相同的图像,并且当您滚动整个块时会加载另一个图像,但都具有该图像。我试过调试它,但我无法弄清楚。
我也试过用 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
这在第一个完整的卷轴中被证明有点缓慢和跳跃,但在那之后一切都很好。
概括
因此,如果有人能指出我正确的方向,我将不胜感激。照片应用程序如何做得这么好?
其他人请注意:[已接受] 答案解决了我的图像出现在错误单元格中的问题,但即使在将图像大小调整为正确大小后,我的滚动仍然不稳定。在将我的代码剥离到最基本的部分之后,我最终发现 UIImage#imageWithContentsOfFile 是罪魁祸首。尽管我使用这种方法预热了 UIImages 的缓存,但 UIImage 似乎在内部做了某种惰性缓存。在更新了我的缓存预热代码以使用此处详述的解决方案 - stackoverflow.com/a/10664707/71810 - 我终于获得了超级流畅的 ~60FPS 滚动。