首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何提高包含大量小图片的UCollectionView的性能?

如何提高包含大量小图片的UCollectionView的性能?
EN

Stack Overflow用户
提问于 2015-08-03 18:57:37
回答 7查看 2.8K关注 0票数 17

在我的iOS应用程序中,我的UICollectionView可以显示大约1200个小图像(35x35点)。图像存储在应用程序包中。

我正确地重用了UICollectionViewCell,但仍然存在性能问题,这取决于我如何处理图像加载:

  • My app是应用程序扩展,那些内存有限的应用程序(本例中为40 MB )。将所有1200个图像放入资产目录并使用UIImage(named: "imageName")加载它们会导致内存崩溃-系统缓存的图像会填满内存。在某些情况下,应用程序需要分配更大的内存部分,但由于缓存的图像,这些内存不可用。操作系统没有触发内存警告和清除缓存,而是直接终止了应用程序。
  • I更改了方法,以避免图像缓存。我将图像作为png文件放到我的项目中(而不是放到asssets目录中),现在我正在使用NSBundle.mainBundle().pathForResource("imageName", ofType: "png")加载它们。应用程序不再因为内存错误而崩溃,但加载单个图像的时间要长得多,即使在最新的iPhones上,快速滚动也会滞后。

我可以完全控制图像,可以将它们转换为.jpeg或优化它们(我已经尝试了ImageOptim和其他一些选项,但都没有成功)。

如何一次解决这两个性能问题?

EDIT 1:

我还尝试在后台线程中加载图像。这是我的UICollectionViewCell子类中的代码

代码语言:javascript
复制
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的建议显示这些图像。

原因:

  • 考虑不同的屏幕大小和方向。它们中的每一个都必须有预先合成的图像,这将使应用程序的下载量很大。
  • 小图像可以按不同的顺序显示,其中一些可能在某些情况下被隐藏。我肯定不能为每个可能的组合和订单预先合成图像。
EN

回答 7

Stack Overflow用户

发布于 2015-08-07 13:00:21

我可以提出另一种方法,或许可以解决你的问题:

考虑将图像块渲染为单个合成图像。如此大的图像应该覆盖应用程序窗口的大小。对于用户来说,它将看起来像小图像的集合,但从技术上讲,它将是大图像的表。

您的当前布局:

代码语言:javascript
复制
 |      |      |      |
 | 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
 |      |      |      |

建议的布局:

代码语言:javascript
复制
 |                    |
 |     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%解决了你的问题,但尝试一下是有意义的。

票数 5
EN

Stack Overflow用户

发布于 2015-08-11 20:58:46

  1. 不需要在每次单元格出现时都从文档目录中获取图像。获取图像后,可以将其保存在NSCache中,下次只需从NSCache获取图像,而不是再次从document directory.
  2. Create object NSCache objCache中获取图像;
    1. in your cellForItemAtIndexPath,只需记下

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];});} }];});}

  • 获取镜像后,在NSCache中设置路径。下一次它将检查是否已经下载,然后只从缓存设置。

如果你需要任何帮助,请告诉我。

谢谢!

票数 5
EN

Stack Overflow用户

发布于 2015-08-03 20:40:59

从PNG更改为JPEG无助于节省内存,因为当您将图像从文件加载到内存时,会将其从压缩数据中提取为未压缩的字节。

对于性能问题,我建议您异步加载图像,并使用委托/块更新视图。并在内存中保留一些图像(但不是所有图像,比方说100)

希望这能有所帮助!

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31785490

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档