Index of TableView/CollectionView cells

UITableView/UICollectionView cell interaction

Getting the index of the selected cell is an easy thing to do, but I’ve seen so many implementations that use very weird ways in order to get the selection especially if the cell has many buttons/Actions to get its index.


Let’s get back to the basics, how do you get the index of a selected cell?

By simply implementing the tableview/collection view delegate

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {} 
Or: 
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}

Those are the basic way to get a selection from the user, but what If I have a single button in the cell? I believe it’s not worth it to worry about the button, make it unresponsive to the touches and just let the above selection delegates handle it for you!

But what if I want that specific button? If I had multiple buttons that trigger multiple actions in my business logic? that’s where comes the two ways of handling touches in view and inform your viewController about it: Delegates & Closures!

I myself find closures a very bad & ugly way to connect views to their parents, plus developers tend to forget using weak or unowned in order to stop the two classes from referencing each other which introduce memory leaks easily. So I believe delegates provides more convenient and clear way to show interaction easily, easier to read, fix, and even generic it through your app.


To use delegates people would send the indexPath as a variable to the cell, so it knows where it falls within its controller, which is a fallout since now the view knows more than it should know. Where it’s very easy to obtain that info from the collection/tableview that manages it via a simple function call:

tableView.indexPath(for: UITableViewCell)

So let’s write a protocol for that purpose: Informing controller with user interaction.

protocol TableViewCellDelegate: class {
  func didSelect(_ cell: UITableViewCell ,_ button: UIButton)
}

How about collections?

protocol CollectionViewCellDelegate: class {
func didSelect(_ cell: UICollectionViewCell ,_ button: UIButton)
}

This one func is all what you gonna need for your different cells, now it returns a reference to your cell, and the button that has been clicked inside it!

We added class so it can be weaken in the cell:

weak var delegate: TableViewCellDelegate?

Add it to your button action:

@IBAction buttonTapped(_ sender: UIButton) {
  delegate?.didSelect(self, sender)
}

And that’s it, make the cell delegate now reference to your controller and your controller confirming to it, and now you’ve all the tabs and interaction with your table/collection cells!


extension UIViewController: TableViewCellDelegate {
  func didSelect(_ cell: UITableViewCell, _ button: UIButton) {
    guard let index = tableView.indexPath(for: cell) else { return }
    let tag = button.tag
  }
}

Here we confirm our viewController to it, get the index, and get the tag of our button to know which button has been clicked, tags can easily set to 0, 1, 2 depending on how many buttons/actions you’ve in your cell.


That should be all for now, hopefully you got my idea so you can use less cluttered protocols through your app that does the same thing through your app, also don’t forget to use extensions to protocols if you want to hide some functions from other controllers that you might need to specify it’s function.

Reach out to me here or on Twitter if you’ve any questions, thanks and have a good coding day!