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!