如何在自定义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 } }
关键改进点说明
- 避免重复绑定Target:在Cell的
awakeFromNib里只绑定一次按钮点击事件,不会因为cell复用重复绑定。 - 通过闭包传递事件:Cell内部处理点击,然后把状态变化通知给ViewController,逻辑更清晰,耦合度更低。
- 正确刷新Cell:状态更新后只刷新对应的cell,而不是调用生命周期方法,性能更好也更符合规范。
- 封装Cell配置:把Cell的UI配置逻辑放到Cell内部的
configure方法里,ViewController只负责传递状态,符合单一职责原则。
内容的提问来源于stack exchange,提问作者user9050369
相关产品推荐
相关产品推荐

