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

如何限制CollectionView单次加载Cell数量?解决内存崩溃问题

解决CollectionView因大量图片下载导致内存崩溃的问题

看起来你遇到了CollectionView加载大量图片引发内存崩溃的典型场景——快速滚动时大量图片并发下载,内存占用瞬间飙升,直接触发系统的内存警告。下面给你一套可行的解决方案,结合你的现有代码来调整:

一、通过滚动状态控制图片加载

核心思路是:快速滚动时暂停非可见Cell的图片下载,滚动停止后再加载当前可见区域的图片,避免一次性发起200个图片请求。

1. 添加滚动状态标记

在你的ViewController里加一个私有属性,标记当前是否处于快速滚动状态:

private var isScrollingFast = false

2. 监听滚动状态变化

实现CollectionView的滚动代理方法,更新滚动状态:

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    isScrollingFast = true
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    isScrollingFast = false
    // 滚动停止后,重新加载可见Cell的图片
    collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate {
        isScrollingFast = false
        collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
    }
}

3. 修改cellForItemAt方法控制图片加载

调整你的图片加载逻辑,只有当Cell可见且未快速滚动时,才发起图片请求:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    // 先优化Cell复用:建议在viewDidLoad里注册Nib,这里直接dequeue即可
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductListCell", for: indexPath) as! ProductListCell
    
    // 默认先设置占位图,避免复用Cell显示旧图片
    cell.imgProduct.image = UIImage(named: "placeholder.png")
    
    // 安全获取产品数据,避免强制解包的崩溃风险
    guard let productsList = self.objCategorywiseResponse.SimilirProductsList,
          indexPath.row < productsList.count,
          let product = productsList[indexPath.row] else {
        return cell
    }
    
    // 判断是否需要加载图片:非快速滚动 + 当前Cell在可见范围内
    let isCellVisible = collectionView.indexPathsForVisibleItems.contains(indexPath)
    if !isScrollingFast && isCellVisible {
        if let urlStr = product.productPic, let url = URL(string: urlStr) {
            cell.imgProduct.sd_setImage(with: url, placeholderImage: UIImage(named: "placeholder.png")) { [weak self] (image, error, _, _) in
                // 加载失败时显示占位图
                if error != nil {
                    cell.imgProduct.image = UIImage(named: "placeholder.png")
                }
            }
        }
    }
    
    // 以下是原有的UI赋值逻辑,优化了解包方式
    cell.lblProductName.text = product.ProductName
    cell.lblProductDesc.text = ""
    
    if let mrp = product.MRP, let discountPrice = product.discountprice {
        cell.lblProductPrice.attributedText = Common().setPrice(Price: mrp, StrikePrice1: discountPrice)
    }
    
    if let discount = product.DiscountPercentage {
        if discount > 0 {
            cell.btnDiscount.setTitle("\(discount)% off", for: .normal)
            cell.btnDiscount.isHidden = false
        } else {
            cell.btnDiscount.isHidden = true
        }
    }
    
    cell.lblRatingdesc.text = product.TotalRating_Reviews
    if let rating = product.AvgRatingCount {
        cell.lblRating.text = "\(rating)"
        cell.subViewRating.rating = rating
    }
    
    cell.lblShopName.text = product.ShopName
    if let distance = product.Distance {
        cell.lblDistance.setTitle("\(distance) km near by you", for: .normal)
    }
    
    cell.lblMobileNo.text = product.LandLineNumber
    
    // 按钮事件绑定
    cell.btnAddToCompare.tag = indexPath.row
    cell.btnAddToCompare.addTarget(self, action: #selector(btnClickedAddToCompare(_:)), for: .touchUpInside)
    cell.btnAddProduct.tag = indexPath.row
    cell.btnAddProduct.addTarget(self, action: #selector(btnClickedAddProduct(_:)), for: .touchUpInside)
    cell.btnCall.tag = indexPath.row
    cell.btnCall.addTarget(self, action: #selector(callCellactn(_:)), for: .touchUpInside)
    cell.btnMap.tag = indexPath.row
    cell.btnMap.addTarget(self, action: #selector(locationCellactn(_:)), for: .touchUpInside)
    
    return cell
}

二、额外的内存优化建议

除了滚动控制,这几个点能进一步降低内存占用:

  • SDWebImage缓存配置:限制内存缓存大小,优先用磁盘缓存,在App启动时添加:
SDImageCache.shared.config.maxMemoryCost = 100 * 1024 * 1024 // 内存缓存上限设为100MB
SDImageCache.shared.config.diskCacheExpireType = .modificationDate
  • 自动压缩大图:SDWebImage支持自动压缩超过ImageView尺寸的图片,添加scaleDownLargeImages选项:
let options = SDWebImageOptions.scaleDownLargeImages
cell.imgProduct.sd_setImage(with: url, placeholderImage: UIImage(named: "placeholder.png"), options: options)
  • 注册Cell复用:在viewDidLoad里提前注册Nib,避免每次cellForItemAt里的nil判断:
collectionView.register(UINib(nibName: "ProductListCell", bundle: nil), forCellWithReuseIdentifier: "ProductListCell")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:53:01