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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 11:24:57