Matt Rajca

blog projects github twitter email

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!