Issue Detecting Button cellForRowAt

The easiest and most efficient way in Swift is a callback closure.

  • Subclass UITableViewCell, the viewWithTag way to identify UI elements is outdated.
  • Set the class of the custom cell to the name of the subclass and set the identifier to ButtonCellIdentifier in Interface Builder.

  • Add a callback property.

  • Add an action and connect the button to the action.

    class ButtonCell: UITableViewCell {
    
        var callback : (() -> Void)?
    
        @IBAction func buttonPressed(_ sender : UIButton) {
           callback?()
        }
    }
    
  • In cellForRow assign the callback to the custom cell.

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonCellIdentifier", for: indexPath) as! ButtonCell
         cell.callback = {
             print("Button pressed", indexPath)  
         }
         return cell
      }
    
  • When the button is pressed the callback is called. The index path is captured.

Edit

There is a caveat if cells can be added or removed. In this case pass the UITableViewCell instance as parameter and get the index path from there

class ButtonCell: UITableViewCell {

    var callback : ((UITableViewCell) -> Void)?

    @IBAction func buttonPressed(_ sender : UIButton) {
       callback?(self)
    }
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonCellIdentifier", for: indexPath) as! ButtonCell
     let item = dataSourceArray[indexPath.row]
     // do something with item
     cell.callback = { cell in
         let actualIndexPath = tableView.indexPath(for: cell)!
         print("Button pressed", actualIndexPath)  
     }
     return cell
  }

If even the section can change, well, then protocol/delegate may be more efficient.

Leave a Comment