We all know a cell is tapped wehn UITableViewDelegate method - tableView:didSelectRowAtIndexPath: - is called.

But what if you have a UIButton in the cell, and the user tap on the button? Or it could be any UIView that can be tapped on in the cell.

In those cases, the buttons or views themselves will handle the touches, and tableView:didSelectRowAtIndexPath: will NOT be called.

Interacting with a UIButton

To handle the interaction with a button, you can create the IBAction in your view controller as per normal (eg. drag the button action selector to your view controller).

The trick is to get the superview of the button, which will be the cell, and then using tableView.indexPathForCell(cell) to get the index path.

@IBAction func tapOnButton(sender: UIButton) {
    let cell = sender.superview as! UITableViewCell
    let indexPath = tableView.indexPathForCell(cell)

Note: This is assuming the button is contained directly in the cell. If the button is contained in another view, then the cell could be sender.superview.superview.

Alternate solutions

This problem is very common, and the “accepted answer” in StackOverflow is one that set the tag of the button during cellForRowAtIndexPath:.

button.tag = indexPath.row

This “misuse” of a view tag means you may not use viewWithTag:, and secondly it could have bug when you delete/move cells (because cellForRowAtIndexPath: will not be called).

Our solution above is simpler, except that you must make sure you know where is UITableViewCell as you travel upwards via superview.

But the best answer is to create your custom UITableViewCell, then have your UIButton delegate the call to your view controller. This requires more code, and a custom class, but is the most correct.

Interacting with any UIView

If you are tapping on any other UIView, you could handle similarly.

But in this case, you cannot add the UIGestureRecognizer to the view on storyboard.

You have to create the gesture with code.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let tapGesture = UITapGestureRecognizer(target: viewController, action: "tapThis:")

To access the view in the selector action tapThis:, use sender.view. Again, make use of superview to find the cell.




Back to Home