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

如何在自定义UITableViewCell中实现双按钮点击切换图标?

解决UITableViewCell中双按钮切换图标的问题

首先得说下你当前代码里的几个关键问题,这些问题会导致按钮点击异常或者逻辑混乱:

  • 重复添加按钮Target:每次cellForRowAt执行时都会给按钮绑定一次点击事件,cell复用后同一个按钮会绑定多个相同的事件,点击一次会触发多次回调。
  • 错误调用生命周期方法:手动调用viewDidLoad()和viewWillAppear(true)是完全错误的,这两个方法由系统自动调用,手动触发会打乱ViewController的正常生命周期流程,完全没必要这么做。
  • 状态更新逻辑不清晰:你现在用全局的kind变量控制按钮状态,但更新状态后没有正确刷新对应的cell,而是试图通过生命周期方法重新加载,这是不合理的。

接下来是优化后的实现方案:

第一步:改进自定义UITableViewCell

我们把按钮的点击事件处理移到Cell内部,然后通过闭包把点击事件传递给ViewController,避免重复绑定Target:

class KindTableViewCell: UITableViewCell {
    // 假设你已经通过IB或者代码创建了这两个按钮
    @IBOutlet weak var recivedButton: UIButton!
    @IBOutlet weak var paidButton: UIButton!
    
    // 定义闭包,用来通知ViewController按钮点击事件
    var onButtonTapped: ((_ isPaid: Bool) -> Void)?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // 只在Cell初始化时绑定一次点击事件
        recivedButton.addTarget(self, action: #selector(recivedButtonTapped), for: .touchUpInside)
        paidButton.addTarget(self, action: #selector(paidButtonTapped), for: .touchUpInside)
    }
    
    // 配置Cell的状态
    func configure(isPaid: Bool) {
        if isPaid {
            recivedButton.setImage(#imageLiteral(resourceName: "green on"), for: .normal)
            paidButton.setImage(#imageLiteral(resourceName: "red off"), for: .normal)
        } else {
            paidButton.setImage(#imageLiteral(resourceName: "red on"), for: .normal)
            recivedButton.setImage(#imageLiteral(resourceName: "green off"), for: .normal)
        }
    }
    
    @objc private func recivedButtonTapped() {
        onButtonTapped?(false)
    }
    
    @objc private func paidButtonTapped() {
        onButtonTapped?(true)
    }
}

第二步:修改ViewController中的代码

现在ViewController只需要维护状态,在按钮点击时更新状态并刷新对应的cell:

// 假设这是你的ViewController类
class YourViewController: UIViewController, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!
    // 如果是全局控制所有cell的状态,用这个变量;如果每个cell独立状态,改成数组比如var cellStates: [Bool] = []
    var kind: Bool = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(KindTableViewCell.self, forCellReuseIdentifier: "kindCell")
        tableView.dataSource = self
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // 根据你的实际返回行数,比如返回1或者你的数据源数量
        return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "kindCell", for: indexPath) as! KindTableViewCell
        // 配置Cell状态
        cell.configure(isPaid: kind)
        // 设置闭包,处理按钮点击
        cell.onButtonTapped = { [weak self] isPaid in
            guard let self = self else { return }
            // 更新状态
            self.kind = isPaid
            // 刷新当前cell,避免整个tableView刷新
            tableView.reloadRows(at: [indexPath], with: .automatic)
        }
        return cell
    }
}

关键改进点说明

  1. 避免重复绑定Target:在Cell的awakeFromNib里只绑定一次按钮点击事件,不会因为cell复用重复绑定。
  2. 通过闭包传递事件:Cell内部处理点击,然后把状态变化通知给ViewController,逻辑更清晰,耦合度更低。
  3. 正确刷新Cell:状态更新后只刷新对应的cell,而不是调用生命周期方法,性能更好也更符合规范。
  4. 封装Cell配置:把Cell的UI配置逻辑放到Cell内部的configure方法里,ViewController只负责传递状态,符合单一职责原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:27:35