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

Swift iOS UICollectionView单元格选中样式切换异常问题

UICollectionView分类选择功能异常修复

核心问题点

当前代码运行不符合预期的根因有3个:

  • 选中状态标记isSelected初始值为nil,未按需求设置默认选中第一个单元格,导致初始加载时无选中项
  • 每次选中状态变更调用全量reloadData(),易触发cell复用导致的样式错位,同时存在不必要的性能损耗
  • 单section场景下混用indexPath.row和indexPath.item取值,写法不规范,极端场景会出现索引匹配错误
  • 未处理cell复用的样式重置,滑动时可能出现选中状态错位

可直接运行的修复代码

1. 修正选中状态属性定义

将原可选类型的选中索引改为非可选,默认值设为0(即第一个单元格),同时优化刷新逻辑,仅刷新状态变化的两个单元格:

var category: [Category] = [
    Category(cat: "For You"),
    Category(cat: "Top chats"),
    Category(cat: "Kids"),
    Category(cat: "Categories"),
    Category(cat: "Editor Choice")
]

// 默认选中第0位的第一个分类
var isSelected: Int = 0 {
    didSet {
        // 仅刷新旧选中项和新选中项,无需全量刷新列表
        let oldIndexPath = IndexPath(row: oldValue, section: 0)
        let newIndexPath = IndexPath(row: isSelected, section: 0)
        collectionView.reloadItems(at: [oldIndexPath, newIndexPath])
    }
}

2. 修正选中点击逻辑

如果需要实现点击切换选中的交互,可按需添加已选中项的点击处理,不需要的话直接赋值即可:

extension DashboardViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // 若不需要点击已选中项做特殊处理,直接保留下面这行赋值即可
        isSelected = indexPath.row
        
        // 可选:如果需要点击已选中项回到默认第一个选中,打开下面注释
        /*
        if isSelected == indexPath.row {
            isSelected = 0
        } else {
            isSelected = indexPath.row
        }
        */
    }
}

3. 统一cell配置逻辑

统一索引取值,直接用分类数据生成富文本,避免从label取文本可能出现的空值问题:

extension DashboardViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return category.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        configureDashboardCell(collectionView, cellForItemAt: indexPath)
    }
    
    func configureDashboardCell(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CategoryCell.self), for: indexPath) as? CategoryCell else {
            return UICollectionViewCell()
        }
        let currentCat = category[indexPath.row]
        // 选中态样式
        if isSelected == indexPath.row {
            let attr: [NSAttributedString.Key : Any] = [
                .foregroundColor: UIColor.blue,
                .underlineStyle: NSUnderlineStyle.single.rawValue,
                .underlineColor: UIColor.blue
            ]
            cell.lblName.attributedText = NSAttributedString(string: currentCat.cat, attributes: attr)
        } else {
            // 未选中态样式
            let attr: [NSAttributedString.Key : Any] = [
                .foregroundColor: UIColor.gray
            ]
            cell.lblName.attributedText = NSAttributedString(string: currentCat.cat, attributes: attr)
        }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: (collectionView.bounds.width / 3) - 5 , height: 40)
    }
}

4. 补充cell复用重置逻辑(必加,避免滑动错位)

在自定义CategoryCell中重写复用准备方法,重置旧样式:

class CategoryCell: UICollectionViewCell {
    @IBOutlet weak var lblName: UILabel!
    
    override func prepareForReuse() {
        super.prepareForReuse()
        lblName.attributedText = nil
        lblName.text = nil
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:09:28