Matt Rajca

Detecting Long Presses on UIBarButtonItems

September 07, 2016

Sometimes you may want to distinguish between a quick tap and a long press on a UIBarButtonItem. Many iOS document-based apps, for example, contain an Undo button that undos the last action when tapped quickly and displays an undo/redo menu when long-pressed. Since UIBarButtonItem doesn’t inherit from UIView or expose the item’s underlying view, this isn’t as easy as adding a gesture recognizer.

What many people don’t realize is that their IBActions can actually be passed a second parameter containing the UIEvent triggering the action. For example, instead of defining:

@objc private func showUndoMenu(sender: AnyObject) { ...

we can define:

@objc private func showUndoMenu(sender: AnyObject, forEvent event: UIEvent) { ...

Now when the bar button is tapped, we’ll get sent the event that triggered the tap.

From here, we can access the first touch:

	guard let touch = event.allTouches?.first else { ... }

And then we can ask for its tap count. This value will be 1 for quick, single taps, and 0 for long presses that are around 1 second or longer.

	if touch.tapCount == 1 {
		// Handle tap
	} else if touch.tapCount == 0 {
		// Handle long press
	}

While not obvious, that’s all it takes to quickly detect a long press on a UIBarButtonItem!