Be careful when using .onTapGesture()
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
If you’ve been using SwiftUI, there’s a good chance that you’ve called the modifier .onTapGesture()
.
This modifier is very convenient, because it allows you to define a closure that will be called when the user taps on the View
that the modifier has been attached to.
However, you want to be careful when you use this modifier, because it can easily turn into a really bad practice!
So let’s take a look at a couple of use cases.
In this first use case, we use the modifier .onTapGesture()
to start pre-fetching some data over the network when the user taps on a View
.
This is a good use case for the modifier .onTapGesture()
, because it is used to trigger an action that will be completely transparent to the user 👍
Now let’s have a look at a second use case.
Here, the modifier .onTapGesture()
is being used to present a View
modally.
And in this use case, using the modifier .onTapGesture()
is actually a very bad practice!
The reason is that presenting a View
modally is a user-facing action and for user-facing actions it’s much better to use a Button
than to use the modifier .onTapGesture()
.
The reason why is because using a Button
will bring a lot of desirable side-effects.
For example, the action will become visible to the accessibility layer and the view will have a highlighted state that shows a visual feedback when the user presses the Button
.
So remember, even though it can be tempting to call a modifier rather than to wrap a complex View
inside a Button
, if you’re implementing an action that’s visible to the user, then you definitely want to use a Button
.
That’s all for this article, I hope you’ve enjoyed this new format!
Here’s the code if you want to experiment with it:
// First Use Case
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
VStack {
// ...
}
.onTapGesture {
viewModel.prefetchData()
}
}
}
// Second Use Case (Bad)
import SwiftUI
struct ContentView: View {
@State var showModal = false
var body: some View {
VStack {
// ...
}
.sheet(isPresented: $showModal){
// ...
}
.onTapGesture {
showModal = true
}
}
}
// Second Use Case (Good)
import SwiftUI
struct ContentView: View {
@State var showModal = false
var body: some View {
Button {
showModal = true
} label: {
VStack {
// ...
}
}
.sheet(isPresented: $showModal){
// ...
}
}
}