120 - Sequencing Gestures in SwiftUI

Sequencing gestures in SwiftUI allows you to create a series of interactions that respond to user input in a particular order. This provides a more intuitive user experience and allows for more complex and interactive behaviors in your views.

In this chapter, you’ll explore how to sequence a long press gesture followed by a rotation gesture. Think of it as an unlocking mechanism — the user has to unlock the rotation by long pressing on the image. Once the image is unlocked, it can then be rotated.

Let’s dive into the implementation:

// Define the states for rotation
enum RotationState: Equatable {
  case inactive
  case pressing
  case rotating(angle: Angle)

  var angle: Angle {
    switch self {
    case .inactive, .pressing:
      return .zero
    case .rotating(let angle):
      return angle
    }
  }

  var isRotating: Bool {
    switch self {
    case .inactive, .pressing:
      return false
    case .rotating:
      return true
    }
  }
}

struct ContentView: View {
  @GestureState private var rotationState = RotationState.inactive
  @State private var rotationAngle = Angle.zero

  var body: some View {
    let longPressBeforeRotation = LongPressGesture(minimumDuration: 0.5)
      .sequenced(before: RotationGesture())
      .updating($rotationState) { value, state, _ in
        switch value {
          // Long press begins
        case .first(true):
          state = .pressing
          // Long press confirmed, rotation may begin
        case .second(true, let rotation):
          state = .rotating(angle: rotation ?? .zero)
          // Rotation ended or the long press cancelled
        default:
          state = .inactive
        }
      }
      .onEnded { value in
        guard case .second(true, let rotation?) = value else { return }
        self.rotationAngle = rotation
      }

    Image(systemName: "arrow.triangle.2.circlepath")
      .font(.system(size: 100))
      .rotationEffect(rotationState.angle + rotationAngle)
      .foregroundColor(rotationState.isRotating ? .blue : .red)
      .animation(.default, value: rotationState)
      .gesture(longPressBeforeRotation)
  }
}

Your preview should look like this:

You can create custom gestures in SwiftUI.

In this example, users need to perform a long press on the arrow image before they can rotate it. The image will turn blue when it’s rotating and revert to red when it’s not. The rotation angle is kept after the gesture ends, allowing users to rotate the image multiple times.

Here’s a detailed breakdown of the code:

The power of this approach is in its flexibility. By sequencing gestures and managing the state properly, you can create complex interactions that provide a fluid and intuitive user experience.

This example showcases how you can sequence gestures to create complex and interactive UIs in SwiftUI. Sequencing gestures not only provides a more intuitive way to interact with your app, but also allows you to create unique and engaging experiences.