在我的iOS应用程序中,我的UICollectionView
可以显示大约1200个小图像(35x35点)。图像存储在应用程序包中。
我正确地重用了UICollectionViewCell
,但仍然存在性能问题,这取决于我如何处理图像加载:
UIImage(named: "imageName")
加载它们会导致内存崩溃-系统缓存的图像会填满内存。在某些情况下,应用程序需要分配更大的内存部分,但由于缓存的图像,这些内存不可用。操作系统没有触发内存警告和清除缓存,而是直接终止了应用程序。NSBundle.mainBundle().pathForResource("imageName", ofType: "png")
加载它们。应用程序不再因为内存错误而崩溃,但加载单个图像的时间要长得多,即使在最新的iPhones上,快速滚动也会滞后。我可以完全控制图像,可以将它们转换为.jpeg或优化它们(我已经尝试了ImageOptim和其他一些选项,但都没有成功)。
如何一次解决这两个性能问题?
EDIT 1:
我还尝试在后台线程中加载图像。这是我的UICollectionViewCell
子类中的代码
private func loadImageNamed(name: String) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { [weak self] () in
let image = bundle.pathForResource(name, ofType: "png")?.CGImage
if name == self?.displayedImageName {
dispatch_async(dispatch_get_main_queue(), {
if name == self?.displayedImageName {
self?.contentView.layer.contents = image
}
})
}
})
}
这使得滚动平滑,而不消耗额外的内存缓存,但当滚动到某个位置编程时(例如,当UICollectionView
滚动到顶部时)它会引起另一个问题:在滚动动画期间图像没有更新(滚动太快,无法加载),滚动完成后,错误的图像显示了几分之一秒-一个接一个地被正确的图像替换。这在视觉上是非常令人不安的。
EDIT 2:
我无法将小图像分组为较大的合成图像,并按照this answer的建议显示这些图像。
原因:
发布于 2015-08-07 13:00:21
我可以提出另一种方法,或许可以解决你的问题:
考虑将图像块渲染为单个合成图像。如此大的图像应该覆盖应用程序窗口的大小。对于用户来说,它将看起来像小图像的集合,但从技术上讲,它将是大图像的表。
您的当前布局:
| | | |
| cell | cell | cell | -> cells outside of screen
| | | |
************************
*| | | |*
*| cell | cell | cell |* -> cells displayed on screen
*| | | |*
*----------------------*
*| | | |*
*| cell | cell | cell |* -> cells displayed on screen
*| | | |*
*----------------------*
*| | | |*
*| cell | cell | cell |* -> cells displayed on screen
*| | | |*
************************
| | | |
| cell | cell | cell | -> cells outside of screen
| | | |
建议的布局:
| |
| cell with |
| composed image | -> cell outside of screen
| |
************************
*| |*
*| |*
*| |*
*| |*
*| cell with |*
*| composed image |* -> cell displayed on screen
*| |*
*| |*
*| |*
*| |*
*| |*
************************
| |
| cell with |
| composed image | -> cell outside of screen
| |
理想情况下,如果您预先呈现这样的合成图像,并在构建时将它们放入项目中,但您也可以在运行时呈现它们。可以肯定的是,第一个变种的工作速度会快得多。但在任何情况下,单个大图像比单独的图像片段消耗更少的内存。
如果你有可能预先渲染他们,然后使用JPEG格式。在这种情况下,您的第一个解决方案(在主线程上使用[UIImage imageNamed:]
加载图像)可能会工作得很好,因为使用的内存更少,布局也更简单。
如果你必须在运行时渲染它们,那么你将需要使用当前的解决方案(在后台工作),当快速动画发生时,你仍然会看到图像错位,但在这种情况下,它将是单一错位(一个图像覆盖窗口框架),因此它应该看起来更好。
如果你需要知道用户点击了什么图像(原始小图像35x35),你可以使用UITapGestureRecognizer
附加到细胞。当手势被识别时,可以使用locationInView:
方法计算小图像的正确索引。
我不能说它100%解决了你的问题,但尝试一下是有意义的。
发布于 2015-08-11 20:58:46
cachedImage=[ objectForKey:arr_PathFromDocumentDirectoryindexPath.row];if (cachedImage) { imgView_Cell.image = cachedImage;} else { dispatch_queue_t q=cached0ul);dispatch_async(q,^{ /*从文档目录获取图像...*/ [self downloadImageWithURL:arr_PathFromDocumentindexPath.row completionBlock:^(BOOL成功,CGImageRef数据,NSString*路径){ if (成功){ dispatch_async(dispatch_get_main_queue(),^{ UIImage *img =UIImage imageWithCGImage: data;imgView_Cell.image = img;[objCache setObject:img forKey::arr_PathFromDocumentindexPath.row];});} }];});}
如果你需要任何帮助,请告诉我。
谢谢!
发布于 2015-08-03 20:40:59
从PNG更改为JPEG无助于节省内存,因为当您将图像从文件加载到内存时,会将其从压缩数据中提取为未压缩的字节。
对于性能问题,我建议您异步加载图像,并使用委托/块更新视图。并在内存中保留一些图像(但不是所有图像,比方说100)
希望这能有所帮助!
https://stackoverflow.com/questions/31785490
复制相似问题