我有一个自定义UICollectionViewCell与UIImageView作为子视图.我正在使用UIImage.imageWithContentsOfFile :,将文件系统中的图像加载到单元格内的UIImageView中.
我现在已经尝试了很多方法,但是它们已经是bug或者性能问题.
注意:我使用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,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,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,forIndexPath: index_path) cell.image = @preloaded_images[index_path.row] end
在第一个完整的卷轴中,结果变得有点慢,跳跃,但一切顺利.
概要
所以,如果有人能指出我正确的方向,我会非常感激.照片应用程序如何做得很好?
注意其他人:[被接受]的答案解决了我的图像出现在错误的单元格的问题,但即使在调整我的图像大小以正确的大小后,我仍然正在滚动滚动.在将我的代码剥离到裸机之后,我最终发现UIImage#imageWithContentsOfFile是罪魁祸首.即使我使用这种方法预热了UIImages的缓存,UIImage似乎在内部做一些懒惰的缓存.更新我的缓存加温代码以使用这里详细说明的解决方案 – stackoverflow.com/a/10664707/71810 – 我终于有超级光滑〜60FPS滚动.
解决方法
您在操作块中为@image分配整个集合视图类的私有变量.你应该改变:
@image = UIImage.imageWithContentsOfFile(image_path)
至
image = UIImage.imageWithContentsOfFile(image_path)
并更改@image的所有引用以使用局部变量.我愿意打赌,问题是每次创建一个操作块并分配给实例变量时,你正在替换已经存在的内容.由于操作块出现的一些随机性,主队列异步回调正在获得相同的图像,因为它正在访问最后一次@image被分配.
本质上,@image像操作和异步回调块的全局变量一样.