Swift:解决CollectionView切换按钮选中时旧按钮未重置颜色的问题
分类按钮选中状态修复方案
问题描述
ViewController内有一个水平CollectionView,通过按钮展示StoriesModel枚举分类,点击按钮切换TableView的文章分类。需求是:点击按钮时,该按钮背景变为.blue;点击新分类按钮时,新按钮设为.blue,之前选中的按钮重置为.clear。当前问题是点击新按钮时,旧按钮的背景色无法重置。
问题根源分析
- Cell选中状态逻辑倒置:
isSelectedButton的didSet里,isSelectedButton = true时背景设为.clear,和需求(选中为.blue)完全相反。 - 直接设置背景色覆盖状态逻辑:
setUpCategoryCollectionViewCell方法里手动设置按钮背景色,会覆盖isSelectedButton的didSet逻辑,导致状态更新失效。 - 状态控制逻辑冲突:同时使用了
UICollectionViewDelegate的didSelectItemAt和按钮自身的delegate,且手动设置背景色和isSelectedButton属性混用,导致状态管理混乱。 - Cell复用未处理:没有在
prepareForReuse中重置选中状态,滚动时会出现复用错误。
修复步骤
1. 修正StoryCategoryCollectionViewCell的状态逻辑
- 调整
isSelectedButton的didSet逻辑,让选中时背景为.blue,未选中为.clear。 - 移除
setUpCategoryCollectionViewCell中手动设置背景色的代码,统一由isSelectedButton控制。 - 添加
prepareForReuse方法,重置选中状态,避免复用问题。 - 调整按钮点击方法,不再手动切换
isSelectedButton,由ViewController统一控制。
2. 统一ViewController的状态管理逻辑
- 移除
UICollectionViewDelegate的didSelectItemAt方法(因为是按钮点击触发,不需要选中cell本身)。 - 在
StoriesCollectionButtonsDelegate的实现中,只通过修改cell的isSelectedButton属性来更新状态,不再手动设置背景色。 - 确保
selectedButtonIndex初始值正确(比如默认选中第0个,对应Home分类)。
完整修正代码
StoryCategoryCollectionViewCell.swift
//MARK: CollectionView Stories Buttons Protocol protocol StoriesCollectionButtonsDelegate: AnyObject { func didSelectButton(at index: Int) } class StoryCategoryCollectionViewCell: UICollectionViewCell { //MARK: Category Outlets @IBOutlet weak var categoryButton: UIButton! //MARK: Static Properties static let categoryCellIdentifier = "StoryCategoryCollectionViewCell" //MARK: Properties var storiesName: StoriesName? //MARK: Button Delegate weak var delegateButtons: StoriesCollectionButtonsDelegate? //MARK: isSelectedButton - 修正逻辑:选中时背景blue,未选中clear var isSelectedButton: Bool = false { didSet { categoryButton.backgroundColor = isSelectedButton ? .blue : .clear } } //MARK: Awake From Nib override func awakeFromNib() { super.awakeFromNib() setUpStoryCategory() categoryButton.addTarget(self, action: #selector(selectedButton), for: .touchUpInside) } //MARK: 处理Cell复用,重置状态 override func prepareForReuse() { super.prepareForReuse() isSelectedButton = false storiesName = nil } //MARK: CollectionView Stories Buttons Action @objc private func selectedButton() { delegateButtons?.didSelectButton(at: self.tag) // 不再手动切换,由ViewController统一控制 } } //MARK: Category Private Methods extension StoryCategoryCollectionViewCell { private func setUpStoryCategory() { categoryButton.tintColor = .white categoryButton.layer.cornerRadius = categoryButton.frame.size.height / 2 categoryButton.clipsToBounds = true categoryButton.layer.borderColor = UIColor.white.cgColor categoryButton.layer.borderWidth = 2 } func setUpCategoryCollectionViewCell(data: StoriesCategory) { guard let category = StoriesName(rawValue: data.Category ?? 0) else { return } storiesName = category categoryButton.setTitle(category.storiesName, for: .normal) categoryButton.setTitleColor(.white, for: .normal) // 移除手动设置背景色,由isSelectedButton控制 // 初始化时根据是否是默认分类设置选中状态 isSelectedButton = (data.Category == 0) } }
ViewController.swift
// 移除原来的UICollectionViewDelegate的didSelectItemAt方法,因为不需要选中cell本身 //MARK: - UICollectionViewDataSource extension ViewController: UICollectionViewDataSource { // ... 其他数据源代码保持不变 ... else if collectionView == storiesCollection { guard let categoryCell = collectionView.dequeueReusableCell(withReuseIdentifier: StoryCategoryCollectionViewCell.categoryCellIdentifier, for: indexPath) as? StoryCategoryCollectionViewCell else { return UICollectionViewCell() } let item = stories[indexPath.row] categoryCell.setUpCategoryCollectionViewCell(data: item) categoryCell.delegateButtons = self // 设置cell的tag为indexPath.row,确保点击时能拿到正确索引 categoryCell.tag = indexPath.row return categoryCell } return UICollectionViewCell() } //MARK: CollectionView Stories Buttons Delegate extension ViewController: StoriesCollectionButtonsDelegate { // 确保ViewController中声明了selectedButtonIndex: Int? 初始值设为0(默认选中Home) func didSelectButton(at index: Int) { // 重置之前选中的按钮 if let previousIndex = selectedButtonIndex, previousIndex != index { let previousIndexPath = IndexPath(item: previousIndex, section: 0) if let previousCell = storiesCollection.cellForItem(at: previousIndexPath) as? StoryCategoryCollectionViewCell { previousCell.isSelectedButton = false } } // 设置当前选中的按钮 let currentIndexPath = IndexPath(item: index, section: 0) if let currentCell = storiesCollection.cellForItem(at: currentIndexPath) as? StoryCategoryCollectionViewCell { currentCell.isSelectedButton = true // 更新选中的分类 selectedCategory = StoriesName.allCases[index] } selectedButtonIndex = index } }
验证说明
- 初始加载时,Home分类按钮会自动设为
.blue。 - 点击其他分类按钮,当前按钮变为
.blue,之前选中的按钮自动变为.clear。 - 滚动CollectionView时,Cell复用不会导致选中状态错乱。
内容的提问来源于stack exchange,提问作者zmnbgd
相关产品推荐
相关产品推荐

