Responding to gestures: Tapping

Responding to gestures: Tapping

Discover how to respond to tap gestures and get the location of the tap in a SwiftUI app

One of the standard gestures a user can use to interact with the user interface of an app or objects within the app is the tap gesture. In SwiftUI, to allow a view to react to a tap gesture, use the onTapGesture(count:perform:) modifier.

import SwiftUI

struct ContentView: View {

    @State private var circleColor: Color = .orange

    var body: some View {
        VStack {
            Circle()
                .fill(circleColor)
                .frame(width: 250, height: 250)

                .onTapGesture {
                    circleColor = .blue
                }
        }
    }
}

The modifier adds an action to be performed when the view recognizes the tap gesture. In the example above, when the user taps on the Circle view, it calls the code block associated with the gesture.

0:00
/0:08

Using the count parameter, you can also define the number of taps required to trigger the action.

In the example below, by initializing the tap gesture with a count value of two, the action will be performed after two consecutive taps.

struct ContentView: View {

    @State private var circleColor: Color = .orange
    
    var body: some View {
        VStack {
            Circle()
                    .fill(circleColor)
                                .frame(width: 250, height: 250)
                 
                .onTapGesture(count: 2) {
                    circleColor = .blue
                }
        }
    }
}   
0:00
/0:08

Customizing the gesture

For further customization, you can define the tap gesture as a separate object and then associate it with the view using one of the variations of the gesture(_:) modifier.

  • gesture(_:isEnabled:): Defines when the gesture is enabled.
  • gesture(_:name:isEnabled:): Defines when the gesture is enabled and allows naming the gesture.
  • gesture(_:including:): Controls how adding a gesture to the view affects the other gestures that might be associated with it.
    • all: Enables all gestures.
    • gesture: Enables the added gesture and disables all other gestures in the subviews.
    • subviews: Enables all subview gestures but disables the added gesture.
    • none: Disable all gestures in the subviews and the added gesture.

To create a tap gesture, use the TapGesture type.

To associate an action with the newly created tap gesture, use the onEnded(_:) modifier. It defines an action to be triggered when the tap ends, meaning when the user raises the finger.

struct ContentView: View {
    
        @State private var isToggled: Bool = false
    
    var tapGesture = TapGesture()
        .onEnded { _ in
            isToggled.toggle()
        }
    
    var body: some View {
                VStack {
                        Circle()
                        .fill(isToggled ? Color.blue : Color.orange)
                        .frame(width: 250, height: 250)
                        .gesture(tapGesture)
                }
        }
}

Getting the location of the tap

When you need to know the position of the tap on a view, use the SpatialTapGesture object. It recognizes one or more taps and reports their location.

struct ContentView: View {

    private var tapGesture = SpatialTapGesture()
        .onEnded { value in
            print(value.location)
        }
    
    VStack {
        Circle()
            .frame(width: 250, height: 250)
            .gesture(tapGesture)
    }
}

The action closure provides a value of type SpatialTapGesture.Value, which holds a CGPoint value representing the x and y coordinates of the tap or a Point3D value for the location of the tap in a 3D context.

0:00
/0:10

Another option to get the tap’s location is to use the onTapGesture(count:coordinateSpace:perform:) modifier. This modifier allows the definition of the coordinate space from which to receive location values and returns the location of the tap.

struct ContentView: View {
    var body: some View {
        VStack {
                Circle()
                    .fill(Color.orange)
                    .frame(width: 250, height: 250)
                    
                    .onTapGesture(coordinateSpace: .local) { value in
                        print(value)
                    }
            }
    }
}

The coordinate space can be local, which returns the tap’s location in relation to the view associated with it, or global, which uses the entire device screen as a reference.