1

I have a custom UITableViewCell in which I have connected my UIButton using Interface Builder

@IBOutlet var myButton: UIButton!

Under cell configuration of UITableViewController, I have the following code:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

var customCell = tableView.dequeueReusableCell(withIdentifier: self.MY_CELL_IDENTIFIER, for: indexPath) as! myCustomCell

// CONFIGURE OTHER CELL PARAMETERS

customCell.myButton.tag = indexPath.row

customCell.myButton.addTarget(self, action: #selector(myButtonPressed), for: UIControlEvents.touchUpInside)

return customCell

}

Finally, I have

private func myButtonPressed(sender: UIButton!) {
        let row = sender.tag
        print("Button Sender row: \(row)")            
    }

This code is not working, unless I change the function definition to below:

@objc private func myButtonPressed(sender: UIButton!) {
            let row = sender.tag
            print("Button Sender row: \(row)")

        }

Is there a better way to implement UIButton on custom UITableViewCell in Swift 3

Harshit Gupta
  • 251
  • 1
  • 3
  • 10
  • Have you tried to create an action from the interface builder? – LopesFigueiredo Dec 27 '16 at 17:53
  • ditch the private keyword – Saheb Roy Dec 27 '16 at 17:54
  • @LopesFigueiredo - The reason I did not create IBAction is because there will be multiple rows and I will have to detect from which row the button was pressed. – Harshit Gupta Dec 27 '16 at 17:55
  • You are adding target to each button every time you reuse a cell. You only have to add target once if you don't want the method myButtonPressed to be invoked several times when you touch the button. – Gabriel Dec 27 '16 at 17:55
  • @LopesFigueiredo - Thanks. I know how to create IBAction, but to uniquely identity each UIButton from the rows, I tried the approach as detailed above. – Harshit Gupta Dec 27 '16 at 17:57
  • @Saheb Roy - Removing the **private** keyword works, but is this the most efficient way to implement the UIButton functionality for custom UITableViewCell? – Harshit Gupta Dec 27 '16 at 17:58
  • Its not about efficiency, Swift 3.0 allows us to do things in many ways which suits our architecture, all the methods are efficient, its upto you what suits ur architecture most – Saheb Roy Dec 27 '16 at 18:18

3 Answers3

7

I am not a big fan using view tags. Instead of this, you could use the delegate pattern for your viewController to be notified when a button has been hit.

protocol CustomCellDelegate: class {
    func customCell(_ cell: UITableViewCell, didPressButton: UIButton)
}

class CustomCell: UITableViewCell {
    // Create a delegate instance
    weak var delegate: CustomCellDelegate?

    @IBAction func handleButtonPress(sender: UIButton) { 
        self.delegate?.customCell(self, didPressButton: sender)
    }
}

class ViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var customCell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! CustomCell
        // CONFIGURE OTHER CELL PARAMETERS

        //Assign the cell's delegate to yourself
        customCell.delegate = self
        return customCell
    }
}

extension ViewController: CustomCellDelegate {
    // You get notified by the cell instance and the button when it was pressed
    func customCell(_ cell: UITableViewCell, didPressButton: UIButton) {
        // Get the indexPath
        let indexPath = tableView.indexPath(for: cell) 
    }
}
dirtydanee
  • 6,081
  • 2
  • 27
  • 43
1

Yes, there is a smarter and better way to do this. The main problem of your method is that it only work if no insert, delete or move cells operation occurs. Because anyone of these operations can change de indexPath of the cells that were created for a different indexPath.

The system I use is this:

1.- Create a IBAction in your cell class MyCustomCell (With uppercase M. It is a class, so name it properly).

2.- Connect the button to that action.

3.- Declare a protocol MyCustomCellDelegate with, at least, a method

func myCustomCellButtonAction(_ cell:MyCustomCell)

and add a property to MyCustomCell

 var delegate : MyCustomCellDelegate?    

4.- Set the view controller as MyCustomCellDelegate

In the method of MyCustomCell connected to the button invoke the delegate method:

 delegate?.myCustomCellButtonAction( self )

5.- In the view controller, in the method cellForRowAt:

 customCell.delegate = self

6.- In the view controller, in the method myCustomCellButtonAction:

func myCustomCellButtonAction( _ cell: MyCustomCell ) {
    let indexPath = self.tableView.indexPathForCell( cell )

    // ......  continue .....
}
Gabriel
  • 3,319
  • 1
  • 16
  • 21
0

You can also use delegates to do the same.

Directly map the IBAction of button in your custom UITableViewCell Class.

Implement the delegate methods in viewcontroller and On button action call the delegate method from custom cell.

user3608500
  • 835
  • 4
  • 10