iOS development
indexpath.row
UITableView
Swift programming
app development

How to get the indexpath.row when an element is activated?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In a UITableView, the way you get indexPath.row depends on what was activated. If the user tapped the row itself, UIKit already gives you the index path. If the user tapped a control inside the cell, you need either a callback from the cell or a way to map that control back to its row.

Use the Delegate When the Row Was Tapped

If the row itself is selected, the simplest answer is the built-in delegate method.

swift
1import UIKit
2
3final class ItemsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
4    let tableView = UITableView()
5    let items = ["One", "Two", "Three"]
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9        tableView.frame = view.bounds
10        tableView.dataSource = self
11        tableView.delegate = self
12        view.addSubview(tableView)
13    }
14
15    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
16        items.count
17    }
18
19    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
20        let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
21        cell.textLabel?.text = items[indexPath.row]
22        return cell
23    }
24
25    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
26        print("section=\(indexPath.section) row=\(indexPath.row)")
27    }
28}

If your interaction is row selection, do not invent a workaround. This is already the correct API.

Convert a Control Position Back to the Table View

If the tap comes from a button, switch, or other control inside the cell, you do not automatically receive an index path. One common technique is to convert the sender position into the table view’s coordinate system.

swift
1@objc func buttonTapped(_ sender: UIButton) {
2    let point = sender.convert(CGPoint.zero, to: tableView)
3    guard let indexPath = tableView.indexPathForRow(at: point) else { return }
4    print("row=\(indexPath.row)")
5}

This works because indexPathForRow(at:) can tell you which row occupies that point. In cellForRowAt, wire the button target like this:

swift
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)

This approach is practical and works well when the controller owns the action.

Prefer a Cell Callback for Cleaner Design

For more maintainable code, the cell can expose a closure or delegate back to the controller. That avoids view-hierarchy tricks and keeps the flow explicit.

swift
1final class ActionCell: UITableViewCell {
2    let actionButton = UIButton(type: .system)
3    var onTap: (() -> Void)?
4
5    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
6        super.init(style: style, reuseIdentifier: reuseIdentifier)
7        actionButton.setTitle("Action", for: .normal)
8        actionButton.addTarget(self, action: #selector(didTap), for: .touchUpInside)
9        contentView.addSubview(actionButton)
10        actionButton.frame = CGRect(x: 220, y: 8, width: 80, height: 30)
11    }
12
13    required init?(coder: NSCoder) {
14        fatalError("init(coder:) has not been implemented")
15    }
16
17    @objc private func didTap() {
18        onTap?()
19    }
20}

In the controller:

swift
1cell.onTap = { [weak self] in
2    guard let self else { return }
3    print("row=\(indexPath.row)")
4}

In many apps, the best version is to pass the model or item id instead of the row number. Rows can move; model identity is usually more stable.

Avoid Tags for Mutable Tables

A common shortcut is button.tag = indexPath.row, then reading sender.tag later. That can work in a tiny static table, but it becomes fragile with:

  • multiple sections
  • insertions and deletions
  • cell reuse
  • diffable data sources

Once rows move, the stored tag may no longer match the item the user sees on screen.

Common Pitfalls

  • Recomputing the row manually when didSelectRowAt already gives you the full IndexPath.
  • Using tags as if row numbers were permanent identifiers.
  • Forgetting that multi-section tables need section as well as row.
  • Walking the superview chain instead of using a cleaner callback or coordinate conversion.
  • Passing row numbers around when the underlying model object would be a safer reference.

Summary

  • Use didSelectRowAt when the row itself is selected.
  • For controls inside a cell, convert the control position to a table-view point or use a cell callback.
  • Keep the full IndexPath when sections matter.
  • Avoid tag-based solutions in tables that can change over time.
  • When possible, pass the model or item id instead of the row number.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.