Ras*_*sto 17 memory performance image ios uicollectionview
在我的iOS应用程序中,我UICollectionView显示了大约1200个小(35x35点)图像.图像存储在应用程序包中.
我正确地重用了UICollectionViewCells但仍然存在性能问题,这取决于我如何处理图像加载:
我的应用程序是应用程序扩展,那些内存有限(在这种情况下为40 MB).将所有1200个图像放入Assets目录并使用它们加载它们会UIImage(named: "imageName")导致内存崩溃 - 系统缓存的图像会填满内存.在某些时候,应用程序需要分配更大的内存部分,但由于缓存的图像,这些内存不可用.操作系统刚刚杀死了应用程序,而不是触发内存警告和清理缓存.
我改变了方法以避免图像缓存.我将图像作为png文件放到我的项目中(而不是作为asssets目录),我现在正在使用它们加载它们NSBundle.mainBundle().pathForResource("imageName", ofType: "png").该应用程序不再因内存错误而崩溃,但单个图像的加载需要更长时间,即使在最新的iPhone上,快速滚动也会滞后.
我对图像有完全的控制,可以将它们转换为.jpeg或优化它们(我已经尝试过ImageOptim和其他一些选项而没有成功).
如何立即解决这两个性能问题?
编辑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
}
})
}
})
}
Run Code Online (Sandbox Code Playgroud)
这使得滚动平滑而不消耗额外的内存用于缓存,但是当以编程方式滚动到某个位置时(例如,当UICollectionView滚动到顶部时)它会导致另一个问题:在滚动动画期间,图像不会更新(滚动速度太快,无法加载)和之后滚动完成它需要错误的图像显示几分之一 - 并且一个接一个地用正确的图像替换.这在视觉上非常令人不安.
编辑2:
我无法将小图像分组为更大的合成图像,并按照此答案的建议显示.
原因:
我可以提出可能解决您问题的替代方法:
考虑将图像块渲染为单个合成图像.这样大的图像应该覆盖app窗口的大小.对于用户来说,它看起来像是小图像的集合,但从技术上讲它将是大图像的表格.
你当前的布局:
| | | |
| 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
| | | |
Run Code Online (Sandbox Code Playgroud)
拟议布局:
| |
| cell with |
| composed image | -> cell outside of screen
| |
************************
*| |*
*| |*
*| |*
*| |*
*| cell with |*
*| composed image |* -> cell displayed on screen
*| |*
*| |*
*| |*
*| |*
*| |*
************************
| |
| cell with |
| composed image | -> cell outside of screen
| |
Run Code Online (Sandbox Code Playgroud)
理想情况下,如果您预先渲染这样的合成图像并在构建时将它们放到项目中,但您也可以在运行时渲染它们.当然,第一个变体的工作速度要快得多.但无论如何,单个大图像会花费更少的内存,然后分离该图像.
如果您有可能预先渲染它们,则使用JPEG格式.在这种情况下,您的第一个解决方案([UIImage imageNamed:]在主线程上加载图像)可能会运行良好,因为使用的内存更少,布局更简单.
如果你必须在运行时渲染它们,那么你将需要使用当前的解决方案(在后台工作),当你快速动画发生时你仍然会看到图像错位,但在这种情况下它将是单个错位(一个图像覆盖窗口)框架),所以看起来应该更好.
如果您需要知道用户点击了什么图像(原始小图像35x35),您可以使用UITapGestureRecognizer附加到单元格.识别手势时,您可以使用locationInView:方法计算小图像的正确索引.
我不能说它100%解决了你的问题,但尝试是有意义的.
小智 5
在您的cellForItemAtIndexPath中,只需写下
UIImage *cachedImage = [objCache objectForKey:arr_PathFromDocumentDirectory[indexPath.row]];
if (cachedImage) {
imgView_Cell.image = cachedImage;
} else {
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(q, ^{
/* Fetch the image from the Document directory... */
[self downloadImageWithURL:arr_PathFromDocument[indexPath.row] completionBlock:^(BOOL succeeded, CGImageRef data, NSString* path) {
if (succeeded) {
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *img =[UIImage imageWithCGImage:data];
imgView_Cell.image = img;
[objCache setObject:img forKey::arr_PathFromDocument[indexPath.row]];
});
}
}];
});
}
Run Code Online (Sandbox Code Playgroud)如果您需要任何帮助,请告诉我。
谢谢!
从 PNG 更改为 JPEG 无助于节省内存,因为当您将图像从文件加载到内存时,它会从压缩数据中提取到未压缩字节。
对于性能问题,我建议您异步加载图像并使用委托/块更新视图。并在内存中保留一些图像(但不是全部,假设 100 个)
希望这可以帮助!
| 归档时间: |
|
| 查看次数: |
2048 次 |
| 最近记录: |