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
相关产品推荐
相关产品推荐

