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
相关产品推荐
相关产品推荐

