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

UICollectionView快速滑动时多个Cell自动选中 单选功能失效问题排查

问题原因

  • HMenuCell存在严重的复用问题:每次调用create方法时都会重复创建并添加titleLabel,cell被复用多次后,一个cell内会叠加多个UILabel实例,修改选中状态时只会修改最新添加的label,之前残留的旧label还保持之前的选中颜色,快速滑动复用后就会出现多选中的视觉错误。
  • 你在didSelectItemAt中仅修改当前可见的cell状态,如果之前选中的cell已经滑出屏幕,collectionView.cellForItem(at: self.selectedItemIndex)会返回nil,导致旧选中状态没有被重置,后续该cell被复用时就会保留旧的选中状态。
  • createSelection方法每次都会新建bottomLayer添加到layer上,没有提前移除旧的layer,会导致同一个cell叠加多个下划线layer。

修复方案

第一步 修复HMenuCell复用逻辑

将子视图的初始化和布局只在cell初始化时执行一次,create方法仅负责更新内容和选中状态:

class HMenuCell:UICollectionViewCell {
    static let identifier = "HMenuCell"
    private var menuProperties:HMenuProperties!
    private var titleLabel = UILabel()
    private var bottomLayer = CALayer()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupUI()
    }
    
    // 子视图初始化布局只执行一次
    private func setupUI() {
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(titleLabel)
        NSLayoutConstraint.activate([
            titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20),
            titleLabel.topAnchor.constraint(equalTo: self.topAnchor),
            titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor)
        ])
        // 提前把bottomLayer加好,避免重复添加
        bottomLayer.cornerRadius = 2.5
        titleLabel.layer.addSublayer(bottomLayer)
        bottomLayer.isHidden = true
    }

    func createSelection(){
        let myWidth = titleLabel.frame.width-2
        bottomLayer.frame = CGRect(x: 0, y: titleLabel.frame.height+20, width: myWidth, height: 5)
        bottomLayer.backgroundColor = self.menuProperties.underlinerColor.cgColor
        bottomLayer.isHidden = false
        titleLabel.textColor = self.menuProperties.selectedTextColor
    }
    
    func removeSelection(){
        titleLabel.textColor = self.menuProperties.deselectedTextColor
        bottomLayer.isHidden = true
    }
    
    func create(indexPath:IndexPath,isSelected:Bool,menuProperties:HMenuProperties){
        self.menuProperties = menuProperties
        // 只更新label内容,不重复加视图
        titleLabel.text = menuProperties.menuTitles[indexPath.row]
        titleLabel.font = UIFont.systemFont(ofSize: menuProperties.textFontSize)
        titleLabel.backgroundColor = menuProperties.cellBackgroundColor
        if isSelected{
            createSelection()
        } else {
            removeSelection()
        }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        // 布局变化时更新下划线位置,避免frame计算错误
        if !bottomLayer.isHidden {
            createSelection()
        }
    }
}

第二步 修复ViewController中的选中逻辑

首先在collectionView初始化时设置单选属性,选中时主动刷新旧选中和新选中的两个item,保证状态同步:

// 初始化时设置collectionView的单选属性
collectionView.allowsMultipleSelection = false

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // 记录旧选中下标
    let oldIndex = self.selectedItemIndex
    // 更新选中下标
    self.selectedItemIndex = indexPath
    // 刷新新旧两个cell,保证状态正确
    var reloadIndexs = [indexPath]
    if let old = oldIndex {
        reloadIndexs.append(old)
    }
    collectionView.reloadItems(at: reloadIndexs)
    // 回调代理
    self.menuControllerDelegate?.didSelectMenuItem(selectedIndex: indexPath)
}

// 自定义选中逻辑不需要实现didDeselectItemAt、shouldSelectItemAt方法可以直接删除,避免逻辑冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 14:24:03