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

Swift:解决CollectionView切换按钮选中时旧按钮未重置颜色的问题

分类按钮选中状态修复方案

问题描述

ViewController内有一个水平CollectionView,通过按钮展示StoriesModel枚举分类,点击按钮切换TableView的文章分类。需求是:点击按钮时,该按钮背景变为.blue;点击新分类按钮时,新按钮设为.blue,之前选中的按钮重置为.clear。当前问题是点击新按钮时,旧按钮的背景色无法重置。

问题根源分析

  1. Cell选中状态逻辑倒置:isSelectedButton的didSet里,isSelectedButton = true时背景设为.clear,和需求(选中为.blue)完全相反。
  2. 直接设置背景色覆盖状态逻辑:setUpCategoryCollectionViewCell方法里手动设置按钮背景色,会覆盖isSelectedButton的didSet逻辑,导致状态更新失效。
  3. 状态控制逻辑冲突:同时使用了UICollectionViewDelegate的didSelectItemAt和按钮自身的delegate,且手动设置背景色和isSelectedButton属性混用,导致状态管理混乱。
  4. 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
    }
}

验证说明

  1. 初始加载时,Home分类按钮会自动设为.blue。
  2. 点击其他分类按钮,当前按钮变为.blue,之前选中的按钮自动变为.clear。
  3. 滚动CollectionView时,Cell复用不会导致选中状态错乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:32:43