You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift异步加载展示图片求助:相册与Facebook图片批量加载卡顿

解决大量图片加载卡顿与逐张展示问题

咱们先拆解下你当前代码里的核心问题,再一步步给出优化方案:

一、相册图片加载的问题根源

你的grabPhotos()方法里有两个致命的性能问题:

  1. 同步请求阻塞线程:设置了requestOptions.isSynchronous = true,这会让循环里的每张图片请求都同步执行——数千张图片的话,主线程会被完全卡住,直到所有图片都加载完才能响应UI。
  2. 全量刷新CollectionView:每加载完一张图片就调用reloadData(),这会让列表不断重新渲染所有cell,性能损耗极大,而且用户也看不到逐张加载的效果。
  3. 预加载所有图片到内存:把数千张图片都塞进imageArray,不仅加载慢,还会造成巨大的内存压力,甚至可能触发内存警告。

二、修改后的相册加载方案

我们改成按需加载:先快速获取所有图片的元数据(PHAsset),然后在cell即将显示时,再异步请求对应图片,加载完就更新单个cell。

1. 先保存PHAsset列表(替换原有的imageArray)

// 定义数组保存相册图片的元数据,这个操作非常快
var assetsArray: [PHAsset] = []

func grabPhotos() { 
    let fetchOptions = PHFetchOptions() 
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] 
    let fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) 
    
    if fetchResult.count > 0 { 
        // 把所有PHAsset快速存入数组,不会阻塞线程
        assetsArray = fetchResult.objects(at: IndexSet(0..<fetchResult.count))
        // 只刷新一次列表,快速显示占位图
        DispatchQueue.main.async { 
            self.collectionView.reloadData() 
        } 
    } else { 
        print("You have no photos") 
        DispatchQueue.main.async { 
            self.collectionView.reloadData() 
        } 
    } 
}

2. 修改CollectionView的数据源方法

在cell显示时异步请求图片,同时处理cell复用的错乱问题:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
    return assetsArray.count 
} 

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath) as! GalleryCollectionViewCell 
    
    // 先设置占位图,避免复用旧图片
    cell.gelleryImages.image = UIImage(named: "placeholder")
    
    let asset = assetsArray[indexPath.row]
    let imgManager = PHImageManager.default() 
    let requestOptions = PHImageRequestOptions() 
    requestOptions.isSynchronous = false // 必须异步请求
    requestOptions.deliveryMode = .fastFormat // 可选:先加载低清图快速占位,再替换高清图
    requestOptions.isNetworkAccessAllowed = true // 允许加载iCloud中的图片
    
    // 给cell打标记,防止滚动时复用cell导致图片错乱
    cell.tag = indexPath.row
    
    imgManager.requestImage(for: asset, targetSize: CGSize(width: 200, height:200), contentMode: .aspectFill, options: requestOptions) { [weak self] (image, error) in 
        guard let self = self else { return }
        // 只有当前cell的标记和请求的indexPath一致时,才更新图片
        if cell.tag == indexPath.row {
            DispatchQueue.main.async { 
                cell.gelleryImages.image = image ?? UIImage(named: "placeholder")
            }
        }
    }
    
    return cell 
}

三、Facebook图片加载的通用优化思路

你的Facebook图片加载应该也是犯了同样的错误:一次性发起所有下载请求,等全部完成再刷新列表。优化逻辑和相册一致:

  1. 先获取所有图片的URL列表,保存到数组,立即刷新CollectionView显示占位图。
  2. 在cellForItemAt中,针对当前cell对应的URL发起异步下载请求。
  3. 处理cell复用问题(比如用tag或绑定URL),避免图片错乱。
  4. 推荐使用成熟的图片缓存库(比如Kingfisher、SDWebImage),它们已经封装了异步加载、缓存、复用处理的逻辑,一行代码就能搞定:
// 假设你有一个保存Facebook图片URL的数组:facebookImageURLs
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath) as! GalleryCollectionViewCell 
    let url = facebookImageURLs[indexPath.row]
    // 用Kingfisher异步加载+缓存图片,自动处理复用
    cell.gelleryImages.kf.setImage(with: url, placeholder: UIImage(named: "placeholder"))
    return cell 
}

四、核心优化总结

  • 永远不要同步请求大量资源,必须用异步操作避免阻塞主线程。
  • 拒绝预加载所有图片到内存,按需加载能大幅降低初始等待时间和内存占用。
  • 处理好CollectionView的cell复用问题,避免滚动时出现图片错乱。
  • 网络图片优先使用成熟的缓存库,节省开发时间的同时提升稳定性。

内容的提问来源于stack exchange,提问作者Matt199

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:23:16