UICollectionView调用reloadData后异常显示多余图片求助
问题根源与解决方案
核心问题
你遇到的多余图片问题,本质是UICollectionView的单元格复用机制导致的内容堆叠:
- 每次调用
cellForItemAt时,你直接给cell的contentView添加新UIImageView、给collectionView添加新按钮,还给cell的layer添加新虚线层; - 当单元格被复用时,这些旧的子视图/图层没有被清除,新内容叠加在旧内容上,就出现了多余图片。
具体修复方案
1. 自定义UICollectionViewCell(推荐方案)
不要在cellForItemAt里动态创建子视图,提前在自定义Cell中布局好所有需要的控件,通过配置Cell状态来切换显示内容,从根源避免重复添加。
自定义Cell代码
import UIKit class PhotoCollageCell: UICollectionViewCell { static let reuseIdentifier = "imageUserCell" // 预定义所有需要的控件 private let imageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let actionButton: UIButton = { let btn = UIButton(type: .system) btn.layer.cornerRadius = 15 btn.layer.masksToBounds = true btn.layer.shadowColor = UIColor.black.cgColor btn.layer.shadowOffset = .zero btn.layer.opacity = 1 btn.layer.shadowRadius = 10 btn.translatesAutoresizingMaskIntoConstraints = false return btn }() private var dottedBorderLayer: CAShapeLayer? override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } private func setupUI() { layer.cornerRadius = 10 layer.masksToBounds = true contentView.addSubview(imageView) contentView.addSubview(actionButton) // 自动布局约束 NSLayoutConstraint.activate([ imageView.topAnchor.constraint(equalTo: contentView.topAnchor), imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), actionButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5), actionButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5), actionButton.widthAnchor.constraint(equalToConstant: 30), actionButton.heightAnchor.constraint(equalToConstant: 30) ]) } // 复用前重置所有状态 override func prepareForReuse() { super.prepareForReuse() imageView.image = nil imageView.isHidden = true actionButton.setImage(nil, for: .normal) actionButton.backgroundColor = nil actionButton.layer.borderWidth = 0 dottedBorderLayer?.removeFromSuperlayer() dottedBorderLayer = nil backgroundColor = nil } // 配置有图片的Cell状态 func setupWithImage(_ image: UIImage, deleteAction: UIAction) { imageView.image = image imageView.isHidden = false actionButton.backgroundColor = .white actionButton.setImage(UIImage(named: "DeletePhoto1"), for: .normal) actionButton.tintColor = .gray actionButton.layer.borderWidth = 0.5 actionButton.layer.borderColor = UIColor.gray.cgColor actionButton.removeAllActions() actionButton.addAction(deleteAction, for: .touchUpInside) } // 配置空Cell状态 func setupEmpty(addAction: UIAction) { backgroundColor = UIColor(named: "PhotoCollage") // 添加虚线边框 let borderLayer = CAShapeLayer() borderLayer.strokeColor = UIColor.gray.cgColor borderLayer.lineDashPattern = [10,4] borderLayer.frame = bounds borderLayer.opacity = 0.4 borderLayer.lineWidth = 5 borderLayer.fillColor = nil borderLayer.path = UIBezierPath(rect: borderLayer.bounds).cgPath layer.addSublayer(borderLayer) dottedBorderLayer = borderLayer actionButton.backgroundColor = UIColor(named: "MainAppColor") actionButton.setImage(UIImage(named: "Plus"), for: .normal) actionButton.tintColor = .white actionButton.removeAllActions() actionButton.addAction(addAction, for: .touchUpInside) } }
2. 修改ViewController代码
import UIKit class SettingsPhotoViewController: UIViewController { @IBOutlet weak var collectionPhotoView: UICollectionView! let imagePicker = UIImagePickerController() var imageArr = [UIImage(named: "1")!,UIImage(named: "2")!,UIImage(named: "3")!,UIImage(named: "4")!] override func viewDidLoad() { super.viewDidLoad() collectionPhotoView.delegate = self collectionPhotoView.dataSource = self // 注册自定义Cell collectionPhotoView.register(PhotoCollageCell.self, forCellWithReuseIdentifier: PhotoCollageCell.reuseIdentifier) imagePicker.delegate = self imagePicker.allowsEditing = false } } extension SettingsPhotoViewController : UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 9 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollageCell.reuseIdentifier, for: indexPath) as! PhotoCollageCell if indexPath.row < imageArr.count { // 配置有图片的Cell let deleteAction = UIAction { [weak self] _ in print("Delete photo at index \(indexPath.row)") // 这里可以添加实际删除逻辑:移除数组元素后刷新 // self?.imageArr.remove(at: indexPath.row) // self?.collectionPhotoView.reloadData() } cell.setupWithImage(imageArr[indexPath.row], deleteAction: deleteAction) } else { // 配置空Cell let addAction = UIAction { [weak self] _ in print("Add photo tapped") // 这里可以添加图片选择逻辑,比如弹出imagePicker // self?.present(self?.imagePicker ?? UIImagePickerController(), animated: true) self?.collectionPhotoView.reloadData() } cell.setupEmpty(addAction: addAction) } return cell } // 以下布局方法保持不变 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: (collectionView.frame.size.width / 3) - 14 , height: (collectionView.frame.height / 3) - 20) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 15 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 0, left: 3, bottom: 0, right: 10) } } // 补充图片选择器代理实现 extension SettingsPhotoViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let selectedImage = info[.originalImage] as? UIImage { imageArr.append(selectedImage) collectionPhotoView.reloadData() } picker.dismiss(animated: true) } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } }
关键修复点
- 控件复用管理:通过
prepareForReuse方法在Cell复用前重置所有状态,彻底清除旧内容; - 控件归属修正:按钮作为Cell的子视图,随Cell一起复用,不会在CollectionView上重复堆叠;
- 状态分离:用专门的方法配置Cell的两种状态,逻辑清晰,避免条件判断混乱。
内容的提问来源于stack exchange,提问作者Denim
相关产品推荐
相关产品推荐

