【发布时间】:2012-12-01 15:28:14
【问题描述】:
我在一个显示大量照片 (50-200) 的应用程序中使用 UICollectionView,但我无法让它变得活泼(例如像照片应用程序一样活泼)。
我有一个自定义 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
这看起来很有希望,但有一个我无法追踪的错误。在初始视图加载时,所有图像都是相同的图像,并且当您滚动整个块时会加载另一个图像,但都具有该图像。我试过调试它,但我无法弄清楚。
我也尝试用Dispatch::Queue.concurrent.async { ... } 替换 NSOperation,但这似乎是一样的。
如果我能让它正常工作,我认为这可能是正确的方法。
方法#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 滚动。
【问题讨论】:
标签: ios cocoa-touch ios6 uicollectionview rubymotion