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

