UITableView加载Assets背景图初始滚动卡顿问题咨询
解决UITableView首次滚动因加载Assets大图导致的FPS下降问题
这问题我之前帮不少开发者踩过坑——核心原因就是首次加载高分辨率大图时的磁盘IO阻塞+内存开销拖慢了主线程,毕竟你每张图1.8-3MB、3x下1125x295的尺寸,直接在cell复用的同步流程里加载,肯定会让滚动帧率掉下来。而首次加载后流畅,是因为系统已经把图片缓存到内存里了,不用再读磁盘。
下面给你几个针对性的解决方案,按优先级从高到低来:
1. 先给图片“瘦身”——最根本的优化
你当前的图片尺寸和大小其实是冗余的:
- 先确认cell的实际显示尺寸:如果你的cell高度远小于295,那3x下的图片完全不需要1125x295,比如cell高度是100,3x图做300x100就足够,用Preview或Photoshop裁剪压缩后,图片大小能降到几百KB,加载速度直接提升好几倍。
- 替换图片格式:把PNG/JPG换成WebP(iOS14+原生支持),压缩比能到原大小的1/3甚至更低,画质损失几乎可忽略,直接替换Assets里的资源就行。
2. 异步加载+自定义缓存——避免主线程阻塞
绝对不要在cellForRowAt里同步调用UIImage(named:)!这个方法首次加载时会阻塞主线程读磁盘。换成异步加载+自己控制缓存,同时解决cell复用的图片错位问题:
// 全局缓存,用NSCache自动管理内存释放 let imageCache = NSCache<NSString, UIImage>() func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID", for: indexPath) as! YourCellType let imageName = yourImageNameArray[indexPath.row] // 先设占位图,避免复用错位 cell.backgroundImageView.image = UIImage(named: "placeholder-bg") // 先查缓存,有就直接用 if let cachedImage = imageCache.object(forKey: imageName as NSString) { cell.backgroundImageView.image = cachedImage return cell } // 异步加载图片 DispatchQueue.global(qos: .userInitiated).async { guard let image = UIImage(named: imageName) else { return } // 缓存图片 self.imageCache.setObject(image, forKey: imageName as NSString) // 切回主线程更新UI DispatchQueue.main.async { // 再次确认cell还是当前indexPath的,避免复用后设置错图片 guard let currentCell = tableView.cellForRow(at: indexPath) as? YourCellType else { return } currentCell.backgroundImageView.image = image } } return cell }
3. 开启预加载——提前把图片读到缓存里
利用UITableView的prefetchDataSource,提前加载即将显示的cell的图片,让用户滚动到之前就把图片缓存好:
// 先设置预代理 tableView.prefetchDataSource = self // 实现预加载方法 extension YourViewController: UITableViewDataSourcePrefetching { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { let imageName = yourImageNameArray[indexPath.row] // 缓存里没有才预加载 if imageCache.object(forKey: imageName as NSString) == nil { DispatchQueue.global(qos: .userInitiated).async { guard let image = UIImage(named: imageName) else { return } self.imageCache.setObject(image, forKey: imageName as NSString) } } } } }
4. 额外的渲染优化
如果做完上面的优化还有小卡顿,可以试试开启光栅化,把cell的渲染结果缓存成位图,减少重复渲染开销:
// 在cell的初始化或配置方法里设置 cell.backgroundImageView.layer.shouldRasterize = true cell.backgroundImageView.layer.rasterizationScale = UIScreen.main.scale
注意一定要设置rasterizationScale为屏幕分辨率,否则图片会模糊。
按这个流程优化下来,首次滚动的FPS问题应该就能彻底解决了。
内容的提问来源于stack exchange,提问作者Michael Neas
相关产品推荐
相关产品推荐

