iOS开发:如何通过UITableViewCell内按钮删除对应单元格
解决UITableViewCell内按钮删除对应单元格的问题
以下是完整的解决方案,按步骤实现代理回调和删除逻辑:
1. 给ExampleCell添加代理协议与按钮事件
修改ExampleCell类,添加代理协议定义、代理属性,并为removeButton绑定点击回调:
import UIKit // 定义代理协议,声明删除回调方法 protocol ExampleCellDelegate: AnyObject { func exampleCellDidTapRemove(_ cell: ExampleCell) } final class ExampleCell: UITableViewCell { // 代理属性,用weak避免循环引用 weak var delegate: ExampleCellDelegate? private let productImageView: UIImageView = .init(frame: .zero) private let categoryLabel: UILabel = .init(frame: .zero) private let nameLabel: UILabel = .init(frame: .zero) private let priceLabel: UILabel = .init(frame: .zero) private let sizeLabel: UILabel = .init(frame: .zero) private let colorLabel: UILabel = .init(frame: .zero) private let removeButton = UIButton() private let clearImage = UIImage(named: "Image-1") let qtyButton = UIButton() private let arrowQty = UIImage(named: "Arrow_Qty") private let wishButton = UIButton() override init(style: UITableViewCell.CellStyle,reuseIdentifier: String?) { super.init(style: .default,reuseIdentifier: "ExampleCell") print("New cell has been create") setupUI() setupConstraints() // 绑定按钮点击事件 setupButtonActions() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(product: Product) { productImageView.image = product.image categoryLabel.text = product.category nameLabel.text = product.name priceLabel.text = "\(product.price)€" sizeLabel.text = "Size: \(product.size)" colorLabel.text = "Color: \(product.color)" } private func setupUI() { selectionStyle = .none let array = [productImageView, categoryLabel, nameLabel, priceLabel, sizeLabel, colorLabel, removeButton, qtyButton, wishButton] array.forEach { view in view.translatesAutoresizingMaskIntoConstraints = false addSubview(view) } contentView.translatesAutoresizingMaskIntoConstraints = false productImageView.layer.cornerRadius = 16 categoryLabel.font = FontStyle.uiS.baseUIFont categoryLabel.textColor = UIColor(named: "GreyScale_400") nameLabel.font = FontStyle.displayMBold.baseUIFont nameLabel.textColor = UIColor(named: "GreyScale_700") priceLabel.font = FontStyle.uiS.baseUIFont priceLabel.textColor = UIColor(named: "GreyScale_700") sizeLabel.font = FontStyle.uiXS.baseUIFont sizeLabel.textColor = UIColor(named: "GreyScale_700") colorLabel.font = FontStyle.uiXS.baseUIFont colorLabel.textColor = UIColor(named: "GreyScale_700") removeButton.setImage(clearImage, for: .normal) qtyButton.setTitle("Qty:1 ", for: .normal) qtyButton.setImage(arrowQty, for: .normal) qtyButton.semanticContentAttribute = .forceRightToLeft qtyButton.layer.cornerRadius = 16 qtyButton.backgroundColor = UIColor(named: "GreyScale_White") qtyButton.setTitleColor(UIColor(named: "GreyScale_700"), for: .normal) qtyButton.layer.borderWidth = 1 qtyButton.layer.borderColor = UIColor(named: "GreyScale_400")?.cgColor qtyButton.titleLabel?.font = FontStyle.uiM.baseUIFont wishButton.setTitle("Move to wishlist", for: .normal) wishButton.setTitleColor(UIColor(named: "GreyScale_700"), for: .normal) wishButton.titleLabel?.font = FontStyle.uiM.baseUIFont let attributedTitle = NSAttributedString(string: "Move to wishlist", attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue]) wishButton.setAttributedTitle(attributedTitle, for: .normal) } private func setupConstraints() { NSLayoutConstraint.activate([ contentView.widthAnchor.constraint(equalToConstant: 350), contentView.heightAnchor.constraint(equalToConstant: 242), productImageView.topAnchor.constraint(equalTo: contentView.topAnchor), productImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), productImageView.widthAnchor.constraint(equalToConstant: 106), productImageView.heightAnchor.constraint(equalToConstant: 114), categoryLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 18), categoryLabel.leadingAnchor.constraint(equalTo: productImageView.trailingAnchor, constant: 32), categoryLabel.widthAnchor.constraint(equalToConstant: 29), categoryLabel.heightAnchor.constraint(equalToConstant: 14), nameLabel.topAnchor.constraint(equalTo: categoryLabel.bottomAnchor, constant: 8), nameLabel.leadingAnchor.constraint(equalTo: productImageView.trailingAnchor, constant: 32), nameLabel.widthAnchor.constraint(equalToConstant: 159), nameLabel.heightAnchor.constraint(equalToConstant: 24), priceLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 8), priceLabel.leadingAnchor.constraint(equalTo: productImageView.trailingAnchor, constant: 32), priceLabel.widthAnchor.constraint(equalToConstant: 56), priceLabel.heightAnchor.constraint(equalToConstant: 14), sizeLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 16), sizeLabel.leadingAnchor.constraint(equalTo: productImageView.trailingAnchor, constant: 32), sizeLabel.widthAnchor.constraint(equalToConstant: 46), sizeLabel.heightAnchor.constraint(equalToConstant: 12), colorLabel.topAnchor.constraint(equalTo: priceLabel.bottomAnchor, constant: 16), colorLabel.leadingAnchor.constraint(equalTo: sizeLabel.trailingAnchor, constant: 16), colorLabel.widthAnchor.constraint(equalToConstant: 62), colorLabel.heightAnchor.constraint(equalToConstant: 12), removeButton.leadingAnchor.constraint(equalTo: contentView.trailingAnchor), removeButton.widthAnchor.constraint(equalToConstant: 12), removeButton.heightAnchor.constraint(equalToConstant: 12), qtyButton.topAnchor.constraint(equalTo: productImageView.bottomAnchor, constant: 32), qtyButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 32), qtyButton.widthAnchor.constraint(equalToConstant: 106), qtyButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 20), qtyButton.heightAnchor.constraint(equalToConstant: 64), wishButton.topAnchor.constraint(equalTo: colorLabel.bottomAnchor, constant: 56), wishButton.leadingAnchor.constraint(equalTo: qtyButton.trailingAnchor, constant: 128), wishButton.widthAnchor.constraint(equalToConstant: 116), wishButton.heightAnchor.constraint(equalToConstant: 16), ]) } // 设置按钮点击事件 private func setupButtonActions() { removeButton.addTarget(self, action: #selector(removeButtonTapped), for: .touchUpInside) } // 触发代理回调 @objc private func removeButtonTapped() { delegate?.exampleCellDidTapRemove(self) } }
2. 在CustomTableViewController中实现代理逻辑
让控制器遵守代理协议,设置cell的代理,并实现删除数据源和刷新表格的逻辑:
// 遵守ExampleCellDelegate协议 extension CustomTableViewController: UITableViewDataSource, ExampleCellDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sneakers.count + 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row == sneakers.count { guard let cell = tableView.dequeueReusableCell(withIdentifier: "OrderSummaryView") as? OrderSummaryView else { print("I'll never print") return OrderSummaryView() } return cell } let product = sneakers[indexPath.row] guard let cell = tableView.dequeueReusableCell(withIdentifier: "ExampleCell") as? ExampleCell else { print("I'll never print") return ExampleCell() } print("Utilize cells") cell.configure(product: product) // 设置cell的代理为当前控制器 cell.delegate = self return cell } // 实现代理方法,处理删除逻辑 func exampleCellDidTapRemove(_ cell: ExampleCell) { guard let indexPath = tableView.indexPath(for: cell) else { return } // 跳过最后一行的OrderSummaryView guard indexPath.row < sneakers.count else { return } // 删除数据源中的对应数据 sneakers.remove(at: indexPath.row) // 带动画删除表格行 tableView.deleteRows(at: [indexPath], with: .automatic) } }
关键说明
- 代理属性用
weak修饰,避免控制器和cell之间的循环引用 - 通过
tableView.indexPath(for: cell)精准获取点击cell的位置,确保删除对应的数据 - 使用
deleteRows(at:with:)方法实现带动画的删除效果,提升用户体验 - 增加
indexPath.row < sneakers.count判断,防止误操作删除最后一行的汇总单元格
内容的提问来源于stack exchange,提问作者Fábio Galante
相关产品推荐
相关产品推荐

