preface

In SwiftUI, we can make our applications more interactive by adding different interactions that respond to our clicks, taps, and swipes.

Today, we review the basic SwiftUI gestures:

  • TapGesture
  • Long press gesture
  • drag
  • Zoom gestures
  • Rotation gestures

TapGesture

The tap gesture allows us to recognize one or more taps on the View. There are several ways to add click gestures.

The first is to use the.ontapgesture modifier directly.

Circle()
  .onTapGesture {
    // Respond to Tap Gesture 
  }
Copy the code

The other option used in the SwiftUI documentation is to create a gesture and configure it as an attribute, then use it with the.gesture (_ : include 🙂 modifier.

Note: To perform an action or respond to a tap, we need to close with the.onended action, which is triggered when the gesture ends.

struct SingleTapGestureView: View {
  var singleTap: some Gesture {
      TapGesture()
          .onEnded { _ in
              // Respond to Tap Gesture}}var body: some View {
      Circle()
          .gesture(singleTap)
  }
}
Copy the code

Actually, I prefer the second approach because we can create different gestures and reuse them through our code.

So if we put the code together, we can start writing something similar.

struct TapGestureView: View {
    @State private var isAnimating = false
    @State private var tapped1x = 0

    var singleTap: some Gesture {
        TapGesture()
            .onEnded { _ in
                tapped1x + = 1

                withAnimation(Animation.easeOut(duration: 0.5)) {
                    self.isAnimating = true
                }

                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self.isAnimating = false}}}var body: some View {
        VStack {
            Text("Tapped 1X: \(tapped1x) times")
                .font(.caption)

            Circle()
                .frame(width: 80, height: 80)
                .foregroundColor(.orange)
                .overlay(
                    Text("1X")
                        .fontWeight(.medium)
                )
                .background(
                    Circle()
                        .strokeBorder(Color.blue, lineWidth: 3)
                        .scaleEffect(isAnimating ? 1.5 : 1)
                        .opacity(isAnimating ? 0 : 1)
                )
                .gesture(singleTap)
        }
    }
}
Copy the code

Similarly, we can control the number of responses by simply using the TapGesture(count: Int) initializer.

In this case, you need to click three times to trigger the.onended action to close.

struct TapGesture3xView: View {
    @State private var isAnimating = false
    @State private var tapped3x = 0

    var multipleTap: some Gesture {
        TapGesture(count: 3)
            .onEnded { _ in
                tapped3x + = 1

                withAnimation(Animation.easeOut(duration: 0.5)) {
                    self.isAnimating = true
                }

                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    self.isAnimating = false}}}var body: some View {
        VStack {
            Text("Tapped 3X: \(tapped3x) times")
                .font(.caption)

            Circle()
                .frame(width: 80, height: 80)
                .foregroundColor(.orange)
                .overlay(
                    Text("3X")
                        .fontWeight(.medium)
                )
                .background(
                    Circle()
                        .strokeBorder(Color.blue, lineWidth: 3)
                        .scaleEffect(isAnimating ? 1.5 : 1)
                        .opacity(isAnimating ? 0 : 1)
                )
                .gesture(multipleTap)
        }
    }
}
Copy the code

Long press gesture

The long press gesture allows us to perform actions after the user has long pressed for a defined amount of time and during the user has long pressed.

We can set a minimum duration to recognize our long press gestures. This can be set in the LongPressGesture initializer.

LongPressGesture(minimumDuration: 2)
Copy the code

We can then use the.updating method to do things during long pressing and the.onended method to do things when our gesture is recognized.

In this example, I will update the size and color of Circle() during the long press operation, and I will display “text done” when the gesture is recognized.

Also, I’m using the GestureState property wrapper here, which is set to true during the long press and false at the end of the gesture. I am using this property wrapper for the example animation.

struct LongPressGestureView: View {
    @GestureState private var isLongPressDetected = false
    @State private var isDone = false

    var longPress: some Gesture {
        LongPressGesture(minimumDuration: 2)
            .updating($isLongPressDetected) { currentState, gestureState, transaction in
                DispatchQueue.main.async {
                    isDone = false
                }
                gestureState = currentState
                transaction.animation = Animation.easeIn(duration: 2)
            }
            .onEnded { done in
                isDone = done
            }
    }

    var body: some View {
        VStack {
            Spacer(a)Circle()
                .frame(width: 10, height: 10)
                .foregroundColor(isLongPressDetected ? .orange : .primary)
                .scaleEffect(CGSize(
                                width: isLongPressDetected ? 10 : 1,
                                height: isLongPressDetected ? 10 : 1))

            Spacer(a)if isLongPressDetected {
                Text("Updating...")}if isDone {
                Text("Done")}Spacer(a)Text("Long Press 2 sec")
                .padding()
                .background(isLongPressDetected ? Color.green : Color.orange)
                .cornerRadius(16)
                .gesture(longPress)
        }
    }
}
Copy the code

drag

The drag gesture allows us to perform actions as we drag the view.

We can take advantage of and use the.onchanged and.onended closing methods to perform certain actions. Both methods give us the nice dragget. Value property, which stores the following drag action information:

  • location
  • predictedEndLocation
  • predictedEndTranslation
  • startLocation
  • time
  • translation

We can use this property to create removable views. In the current example, I use the.onchanged method to update the Circle() position coordinates.

struct DragGestureView: View {
    @State private var location: CGPoint = CGPoint(x: 100, y: 100)

    var drag: some Gesture {
        DragGesture(minimumDistance: 1, coordinateSpace: .local)
            .onChanged { value in
                location = value.location
            }
    }

    var body: some View {
        Circle()
            .frame(width: 100, height: 100)
            .foregroundColor(.orange)
            .position(location)
            .gesture(drag)
    }
}
Copy the code

Here, you add the.onended method to reset the Circle() position coordinates after the drag ends.

struct DragGestureView: View {
    @State private var location: CGPoint = CGPoint(x: 100, y: 100)

    var drag: some Gesture {
        DragGesture(minimumDistance: 1, coordinateSpace: .local)
            .onChanged { value in
                location = value.location
            }
            .onEnded { value in
                withAnimation(.easeOut) {
                    location = CGPoint(x: 100, y: 100)}}}var body: some View {
        Circle()
            .frame(width: 100, height: 100)
            .foregroundColor(.orange)
            .position(location)
            .gesture(drag)
    }
}
Copy the code

Zoom gestures

When we apply the zoom action to the View, the zoom gesture allows for some action.

Here, there are.onchanged and.onended closures that you can use to respond during or at the end of an enlarged action. As attributes, receives is CGFloat MagnificationGesture. Value. We can use this as an example to change the view size.

struct MagnificationGestureView: View {
    @State var magnifiedValue: CGFloat = 1.0

    var magnification: some Gesture {
        MagnificationGesture()
            .onChanged { value in
                magnifiedValue = value
            }
            .onEnded { value in
                magnifiedValue = 1.0}}var body: some View {
        Circle()
            .frame(width: 100 * magnifiedValue, height: 100 * magnifiedValue)
            .foregroundColor(.orange)
            .gesture(magnification)
            .animation(.easeOut)
    }
}
Copy the code

Rotation gestures

The rotation gesture allows you to rotate the view and respond with certain actions during and at the end of the rotation.

It also provides us with.onchanged and.onended closures, which give us rotationGesture. Value, which represents the gesture Angle Value. We can use this value to rotate the view.

struct RotationGestureView: View {
    @State private var angle = Angle(degrees: 0.0)
    @State private var backgroundAngle = Angle(degrees: 0.0)

    var rotation: some Gesture {
        RotationGesture()
            .onChanged { angle in
                self.angle = angle
            }
            .onEnded { angle in
                withAnimation(Animation.spring()) {
                    self.backgroundAngle = angle
                }
            }
    }

    var body: some View {
        Rectangle()
            .frame(width: 150, height: 150, alignment: .center)
            .foregroundColor(.orange)
            .rotationEffect(self.angle)
            .gesture(rotation)
            .background(
                Rectangle()
                    .shadow(color: .primary, radius: 10, x: 0.0, y: 0.01)
                    .foregroundColor(.secondary)
                    .rotationEffect(backgroundAngle)
            )
    }
}
Copy the code

conclusion

Above is a summary of the basic SwiftUI gestures. We can implement more interaction to make our App more lively.

For advanced use, gestures can be combined or used together in response, or you can implement your own custom gestures.

Pay attention to our

Welcome to follow the public account “Swift community”, our vision is to hope more people to learn and use Swift, we will share Swift combat, SwiftUI, Swift foundation as the core technology dry goods

This article has been published in the public account “Swift Community”, if you need to reprint, please add wechat: Fzhanfei, remarks reprint kaibai