【问题标题】:How to get the indexPath.row when I click a button on a table view cell?单击表格视图单元格上的按钮时如何获取 indexPath.row?
【发布时间】:2019-05-03 15:15:20
【问题描述】:

您好,上图是我的待办事项列表应用程序的 UI,现在我只想在单击 tableviewcell 中的详细信息按钮时显示项目的详细信息(第一项、第二项等)。因此,为了获取项目的属性,我需要知道我刚刚单击详细信息按钮的行的indexPath

我已经尝试了 tableview 的一些属性,例如 didSelectRowAtindexPathForSelectedRow,但都不起作用。对于didSelectRowAt,用户需要先点击行,然后点击详细信息按钮,这不是我想要的,indexPathForSelectedRow 对我不起作用。

【问题讨论】:

  • 如果你使用右边的AccessoryButton,你可以调用tableView的委托方法accessoryButtonTappedForRow并获取它的indexPath。
  • 显示您当前的cellForRowAt 代码和您的按钮处理程序。

标签: ios swift uitableview


【解决方案1】:

此类问题的一个常见的通用解决方案是将按钮的@IBAction 连接到单元格中的处理程序(而不是视图控制器中),然后使用委托协议模式以便单元格知道点击按钮时的表格。关键是,当单元格执行此操作时,它将提供对自身的引用,然后视图控制器可以使用该引用来确定适当的 indexPath(从而确定行)。

例如:

  • 给你的UITableViewCell 子类一个协议:

    protocol CustomCellDelegate: class {
        func cell(_ cell: CustomCell, didTap button: UIButton)
    }
    
  • @IBAction 连接到单元(不是视图控制器)并调用委托方法:

    class CustomCell: UITableViewCell {
        weak var delegate: CustomCellDelegate?
    
        @IBOutlet weak var customLabel: UILabel!
    
        func configure(text: String, delegate: CustomCellDelegate) {
            customLabel.text = text
            self.delegate = delegate
        }
    
        @IBAction func didTapButton(_ button: UIButton) {
            delegate?.cell(self, didTap: button)
        }
    }
    
  • 显然,当单元格被创建时,调用configure 方法,传递对自身的引用作为委托:

    extension ViewController: UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            let text = ...
            cell.configure(text: text, delegate: self)
            return cell
        }
    }
    
  • 最后,让委托方法调用indexPath(for:) 来确定相关单元格的索引路径:

    extension ViewController: CustomCellDelegate {
        func cell(_ cell: CustomCell, didTap button: UIButton) {
            guard let indexPath = tableView.indexPath(for: cell) else { return }
    
            // use `indexPath.row` here
        }
    }
    

另一种方法是使用闭包,但同样使用将按钮@IBAction 挂钩到单元格的相同通用模式,但让它调用闭包而不是委托方法:

  • 定义带有闭包的自定义单元格,当点击按钮时将调用该单元格:

    class CustomCell: UITableViewCell {
        typealias ButtonHandler = (CustomCell) -> Void
    
        var buttonHandler: ButtonHandler?
    
        @IBOutlet weak var customLabel: UILabel!
    
        func configure(text: String, buttonHandler: @escaping ButtonHandler) {
            customLabel.text = text
            self.buttonHandler = buttonHandler
        }
    
        @IBAction func didTapButton(_ button: UIButton) {
            buttonHandler?(self)
        }
    }
    
  • 当表格视图数据源创建单元格时,提供一个处理程序闭包:

    extension ViewController: UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
            let text = ...
            cell.configure(text: text, buttonHandler: { [weak self] cell in  // the `[weak self]` is only needed if this closure references `self` somewhere
                guard let indexPath = tableView.indexPath(for: cell) else { return }
    
                // use `indexPath` here
            })
            return cell
        }
    }
    

我个人更喜欢委托协议模式,因为它倾向于更好地扩展,但两种方法都有效。


请注意,在这两个示例中,我特意避免将 indexPath 保存在单元格本身中(或更糟的是,“标记”值)。通过这样做,如果稍后从表中插入和删除行,它可以防止您出现未对齐的情况。


顺便说一句,我使用了相当通用的方法/闭包名称。在真正的应用程序中,您可能会给它们起更有意义的名称,例如didTapInfoButtondidTapSaveButton 等)以阐明功能意图。

【讨论】:

    【解决方案2】:

    实现委托方法tableView(_:accessoryButtonTappedForRowWith:)

    func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
    

    但是,如果您想导航到不同的控制器,请将 segue 连接到附件视图按钮

    如果按钮是自定义按钮,请在 Issue Detecting Button cellForRowAt 中查看我的回答

    【讨论】:

    • 这是我插入单元格的按钮,而不是附件按钮 :(
    • 您应该提到该按钮不是附件按钮。使用回调闭包。请参阅更新答案中的链接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多