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

Swift中UICollectionView水平单选按钮复用异常解决方案咨询

解决UICollectionView单选按钮因复用导致多个红色的问题

嘿,这个坑我之前做电商筛选功能的时候踩过!UICollectionView的复用机制虽然高效,但确实容易让单元格状态“串味”,不过只要把选中状态的管理和单元格刷新逻辑理清楚,分分钟就能解决。下面是具体的实现思路和代码:

核心思路

问题的根源在于:复用的单元格会保留上一次显示的状态,如果只在选中时设置红色,没选中的单元格没重置样式,就会出现多个红色按钮。所以我们需要:

  • 用一个全局变量记录当前选中的索引,不要依赖单元格自身的状态
  • 在单元格显示时,根据这个全局索引统一设置样式
  • 点击选中时,更新索引并刷新必要的单元格(之前选中的+当前选中的)
  • 利用prepareForReuse做双重保险,重置单元格状态

代码实现

1. ViewController中的核心逻辑

class ButtonCollectionVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
    @IBOutlet weak var collectionView: UICollectionView!
    // 关键:记录当前选中的item索引,初始为nil表示没有选中
    private var selectedItemIndex: Int?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCollectionView()
    }
    
    private func setupCollectionView() {
        collectionView.dataSource = self
        collectionView.delegate = self
        // 注册自定义单元格(如果用xib的话换register(nib:forCellWithReuseIdentifier:))
        collectionView.register(SelectableButtonCell.self, forCellWithReuseIdentifier: "SelectableButtonCell")
        // 设置水平布局
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.itemSize = CGSize(width: 120, height: 44)
        layout.minimumInteritemSpacing = 16
        collectionView.collectionViewLayout = layout
    }
    
    // MARK: - UICollectionView DataSource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 8 // 示例数量,根据你的需求调整
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SelectableButtonCell", for: indexPath) as! SelectableButtonCell
        
        // 设置按钮标题
        cell.button.setTitle("选项\(indexPath.row + 1)", for: .normal)
        // 根据全局选中索引设置按钮样式
        let isCurrentSelected = indexPath.row == selectedItemIndex
        cell.button.backgroundColor = isCurrentSelected ? .systemRed : .systemGray5
        cell.button.setTitleColor(isCurrentSelected ? .white : .darkText, for: .normal)
        
        return cell
    }
    
    // MARK: - UICollectionView Delegate
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // 记录之前的选中索引
        let previousSelectedIndex = selectedItemIndex
        // 更新当前选中索引
        selectedItemIndex = indexPath.row
        
        // 准备需要刷新的indexPath数组:当前选中的 + 之前选中的(如果存在)
        var reloadIndexPaths = [indexPath]
        if let prevIndex = previousSelectedIndex {
            reloadIndexPaths.append(IndexPath(row: prevIndex, section: 0))
        }
        
        // 只刷新必要的单元格,比reloadData更高效
        collectionView.reloadItems(at: reloadIndexPaths)
    }
}

2. 自定义单元格(重写prepareForReuse)

class SelectableButtonCell: UICollectionViewCell {
    lazy var button: UIButton = {
        let btn = UIButton(type: .system)
        btn.layer.cornerRadius = 8
        btn.clipsToBounds = true
        btn.titleLabel?.font = UIFont.systemFont(ofSize: 15)
        return btn
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupUI()
    }
    
    private func setupUI() {
        button.frame = bounds
        addSubview(button)
    }
    
    // 关键:复用单元格前重置状态,防止残留上一次的样式
    override func prepareForReuse() {
        super.prepareForReuse()
        button.backgroundColor = .systemGray5
        button.setTitleColor(.darkText, for: .normal)
    }
}

关键注意点

  • 不要依赖单元格的isSelected属性:虽然系统有这个属性,但如果处理不好(比如复用后没同步),还是会出问题,用全局索引更可控。
  • 必须同时处理选中和未选中的样式:在cellForItemAt里,不能只写if 选中就设红色,还要写else 设默认样式,不然复用的单元格会保留之前的红色。
  • 只刷新必要的单元格:用reloadItems(at:)代替reloadData,既提升性能,也避免其他单元格不必要的刷新。
  • prepareForReuse是双重保险:即使前面的逻辑有疏漏,这个方法也能在复用前把单元格重置回默认状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:08:21