Handle Press And Release Events in SwiftUI

Posted in SwiftUI

Updated on March 4th, 2021

⏱ Reading Time: 4 mins

When making apps, there are quite often situations where we want to handle button press and release events in order to trigger additional actions when any of these occur.

Doing that with UIKit on iOS and AppKit on macOS has been quite easy; By either observing for the states of a tap gesture recogniser or by overriding touches and mouse methods (on iOS & macOS respectively) we can pretty fast be notified for any of these two events.

When it comes to SwiftUI, things remain easy, but not that straightforward, and here I’m going to show you how to deal with press and release events.

The Basic How-To

For starters, consider the following super-easy button implementation:

Even though there are tap gesture modifiers that we can apply to the button, they won’t lead to the desired result. What actually does the trick is a drag gesture (!) that we must add to the button. But there’s an important particularity such a gesture must have; the minimum required distance must be set to zero (0), so the gesture to be considered successful instantly.

But wait a minute; a button already handles press gestures and events, and adding a drag gesture just like that might not work as expected. For that reason, it must be passed as an argument to a modifier that allows to attach additional gestures to those that a control already responds to. That is the .simultaneousGesture(_:) modifier:

Now it’s going to become pretty clear why a drag and not a tap gesture:

A drag gesture is providing a modifier that the tap gesture does not have, the onChanged(_:) modifier. Its parameter value is a closure that gets called whenever the gesture state is changed. But in this case where the minimumDistance parameter value is set to 0, it simply signals the beginning of the interaction with the buttons, or in other words when the button is pressed (tapped or clicked).

On the other hand, the onEnded(_:) modifier is the one that through its parameter value notifies when the gesture is finished, or when the button is released in our case here. As you guess, that parameter value is a closure once again.

Putting all that in code ends up to this:

Appending the above in a button allows us to know when the button is actually pressed and released. See the following example:

Making It Even Simpler With A Custom Modifier

The above snippet works great for being notified when a button is pressed or released. However, it introduces some friction as it’s a bit uncomfortable to use as is multiple times.

Fortunately, we can do something better than that; we can create a custom view modifier that will be executing the above code, but it will be shorter to write and easier to remember.

To start, at first we’ll declare the following struct with the default body method:

We will apply the drag gesture to the content argument, which we’ll also return from the method:

Next, we’ll declare the following two closures as stored properties to the struct:

We’ll call the above from the closures of the onChanged(_:) and onEnded(_:) modifiers respectively, so the onPress to be called when the button is pressed, and the onRelease when it’s released:

The custom modifier is ready! Using it requires to initialise a PressActions object and pass it to another modifier called… modifier(_:) as shown next:

That’s definitely shorter to handle press and release events, however it doesn’t feel natural. It would be really great if we could make it look like that:

Well, we actually can do that! How?

By extending the View protocol and creating a brand new method called pressAction. It will be taking as arguments two closures like those we declared in the PressActions struct. Inside its body it will be initialising a PressActions object and providing it as an argument to the modifier(_:) modifier as demonstrated above.

Here it is:

That was the last thing to do! We can now react on press and release events really easily! Of course, we can use all that in conjunction to the default action handler of the button.

The following example changes the button’s background color when pressed:

You can download the demo project from here.

Find the PressActions modifier as a gist.

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.