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

Swift实现点击UITableView单元格内按钮删除对应单元格

实现点击复选框按钮删除UITableView单元格的方案

这问题我熟,咱们可以通过闭包回调的方式来实现——因为自定义Cell本身没法直接操作TableView,得把点击事件传递给管理TableView的ViewController,由它来处理数据删除和界面更新。下面是具体的步骤和修改后的代码:

1. 改造自定义WhishCell,添加回调机制

首先我们要给Cell加两个关键内容:一个用来记录当前Cell在TableView里的位置(索引路径),另一个是回调闭包,用来把按钮点击事件传递给外部。

修改后的WhishCell代码如下:

import UIKit
class WhishCell: UITableViewCell {
    let label: UILabel = {
        let v = UILabel()
        v.font = UIFont(name: "AvenirNext", size: 23)
        v.textColor = .white
        v.font = v.font.withSize(23)
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    let checkButton: UIButton = {
        let v = UIButton()
        v.backgroundColor = .darkGray
        v.translatesAutoresizingMaskIntoConstraints = false
        v.setBackgroundImage(UIImage(named: "boxUnchecked"), for: .normal)
        return v
    }()
    
    // 新增:存储当前cell的索引路径
    var indexPath: IndexPath?
    // 新增:回调闭包,点击按钮后通知外部处理删除逻辑
    var onCheckButtonTapped: ((IndexPath) -> Void)?
    
    public static let reuseID = "WhishCell"
    required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.backgroundColor = .clear
        // add checkButton
        self.contentView.addSubview(checkButton)
        self.checkButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
        self.checkButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
        self.checkButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
        self.checkButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
        self.checkButton.addTarget(self, action: #selector(checkButtonTapped), for: .touchUpInside)
        // add label
        self.contentView.addSubview(label)
        self.label.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 70).isActive = true
        self.label.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    }
    @objc func checkButtonTapped(){
        self.checkButton.setBackgroundImage(UIImage(named: "boxChecked"), for: .normal)
        self.checkButton.alpha = 0
        self.checkButton.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
        UIView.animate(withDuration: 0.3) {
            self.checkButton.alpha = 1
            self.checkButton.transform = CGAffineTransform.identity
        } completion: { [weak self] _ in
            // 动画完成后触发回调,把索引路径传递给外部
            guard let self = self, let indexPath = self.indexPath else { return }
            self.onCheckButtonTapped?(indexPath)
        }
    }
}

2. 在ViewController中处理删除逻辑

接下来在管理TableView的ViewController里,我们需要给Cell设置回调闭包,在闭包里完成删除数据源数据和动画删除单元格的操作。

示例代码如下(假设你已经初始化了TableView并设置了数据源/代理):

class TaskListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    let tableView = UITableView()
    // 假设你的任务数据存在这个数组里(如果有自定义模型,替换成模型数组即可)
    var taskTitles: [String] = ["买牛奶", "整理书架", "写代码"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初始化TableView的基础配置
        tableView.frame = view.bounds
        tableView.register(WhishCell.self, forCellReuseIdentifier: WhishCell.reuseID)
        tableView.dataSource = self
        tableView.delegate = self
        tableView.backgroundColor = .black
        view.addSubview(tableView)
    }
    
    // UITableView数据源方法
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return taskTitles.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath) as! WhishCell
        // 设置任务标题
        cell.label.text = taskTitles[indexPath.row]
        // 给Cell设置当前的索引路径
        cell.indexPath = indexPath
        // 设置回调闭包,处理点击后的删除逻辑
        cell.onCheckButtonTapped = { [weak self] tappedIndexPath in
            guard let self = self else { return }
            // 1. 先删除数据源里对应的数据
            self.taskTitles.remove(at: tappedIndexPath.row)
            // 2. 用动画效果删除TableView里的单元格
            tableView.deleteRows(at: [tappedIndexPath], with: .fade)
        }
        return cell
    }
}

额外优化:用模型对象代替直接传索引路径

如果你的任务有自定义模型(比如包含标题、状态等属性),更推荐传递模型对象而不是索引路径,这样可以避免因为数据变化导致索引路径失效的问题。举个例子:

// 自定义任务模型
struct Task {
    let title: String
    // 可以添加其他属性,比如isCompleted等
}

// ViewController里的数据源改成模型数组
var tasks: [Task] = [
    Task(title: "买牛奶"),
    Task(title: "整理书架"),
    Task(title: "写代码")
]

// 修改Cell的回调闭包类型
var onCheckButtonTapped: ((Task) -> Void)?

// 在ViewController的cellForRow方法里:
let task = tasks[indexPath.row]
cell.label.text = task.title
cell.onCheckButtonTapped = { [weak self] tappedTask in
    guard let self = self else { return }
    // 找到模型在数组里的索引
    if let index = self.tasks.firstIndex(where: { $0.title == tappedTask.title }) {
        self.tasks.remove(at: index)
        tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .fade)
    }
}

这样代码会更健壮,尤其是当你的TableView有排序、过滤等操作时,索引路径可能会频繁变化,用模型对象来定位会更可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:54:39