iOS development
UITableView
long press gesture
Swift
mobile app UI

Long press on UITableView

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Adding a long-press action to a UITableView is a common way to expose extra options such as rename, delete, share, or preview. The basic pattern is to attach a UILongPressGestureRecognizer to the table view, translate the touch location into an index path, and respond only once when the gesture enters the .began state.

Add a UILongPressGestureRecognizer

Start by attaching the gesture recognizer in your view controller:

swift
1import UIKit
2
3final class ItemsViewController: UITableViewController {
4    private let items = ["Alpha", "Beta", "Gamma"]
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8
9        let recognizer = UILongPressGestureRecognizer(
10            target: self,
11            action: #selector(handleLongPress(_:))
12        )
13        recognizer.minimumPressDuration = 0.5
14        tableView.addGestureRecognizer(recognizer)
15    }
16
17    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
18        items.count
19    }
20
21    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
22        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
23            ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
24        cell.textLabel?.text = items[indexPath.row]
25        return cell
26    }
27}

The recognizer is attached to the table view itself, not to each cell. That keeps the implementation simple and works naturally with reused cells.

Map the Touch to a Row

The handler should convert the press location into an IndexPath:

swift
1import UIKit
2
3extension ItemsViewController {
4    @objc private func handleLongPress(_ recognizer: UILongPressGestureRecognizer) {
5        guard recognizer.state == .began else { return }
6
7        let point = recognizer.location(in: tableView)
8        guard let indexPath = tableView.indexPathForRow(at: point) else { return }
9
10        let item = items[indexPath.row]
11        print("Long pressed:", item)
12    }
13}

Using .began is important. A long-press recognizer transitions through several states, and responding on every state change can trigger duplicate actions for a single press.

Present an Action Sheet

In many apps, the long press opens options for the selected row:

swift
1import UIKit
2
3extension ItemsViewController {
4    private func presentActions(for item: String, at indexPath: IndexPath) {
5        let alert = UIAlertController(title: item, message: "Choose an action", preferredStyle: .actionSheet)
6
7        alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ in
8            print("Delete row \(indexPath.row)")
9        })
10
11        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
12        present(alert, animated: true)
13    }
14}

Then call presentActions(for:at:) from handleLongPress.

This pattern keeps the gesture code focused on input recognition while the UI decision stays in a separate helper.

Handle Selection and Gesture Conflicts

Long press and normal row selection can coexist, but you should think about the interaction:

  • tap selects or opens
  • long press shows more actions

If you find the table is feeling too sensitive, tune minimumPressDuration or allowableMovement:

swift
recognizer.minimumPressDuration = 0.6
recognizer.allowableMovement = 10

That helps prevent accidental long-press recognition while the user is simply scrolling or tapping.

Use Haptics for Better Feedback

Long press feels better when the user gets immediate feedback:

swift
1import UIKit
2
3let feedback = UIImpactFeedbackGenerator(style: .medium)
4feedback.impactOccurred()

Trigger the haptic once the gesture is recognized. It makes the interaction feel intentional rather than vague.

Consider UIContextMenuInteraction on Newer iOS

For newer iOS versions, context menus are often a better fit than a custom long-press action sheet. However, a long-press recognizer is still useful when:

  • you support older patterns
  • you need custom behavior unrelated to menus
  • you want direct control over gesture timing

The important point is to choose the interaction that matches the product, not just the first API you remember.

Common Pitfalls

  • Handling every recognizer state instead of only .began, which causes duplicate actions.
  • Trying to add separate long-press recognizers to each cell and fighting cell reuse.
  • Ignoring the case where the user presses empty space and indexPathForRow(at:) returns nil.
  • Making the press duration too short and accidentally interfering with normal scrolling.
  • Packing all business logic directly into the gesture handler instead of forwarding to a dedicated action method.

Summary

  • Add one UILongPressGestureRecognizer to the table view, not one per cell.
  • Convert the touch location into an index path to identify the pressed row.
  • React only when the recognizer enters the .began state.
  • Use the gesture to present actions or context-specific UI for the selected item.
  • Tune the recognizer so long press complements normal table interaction instead of fighting it.

Course illustration
Course illustration

All Rights Reserved.