如何限制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
相关产品推荐
相关产品推荐

