TechnologiesSwiftUI

GestureState struct

iOSmacOStvOSwatchOSvisionOSiOS 13.0+✓ renders

A property wrapper type that updates a property while the user performs a

How it works

GestureState is a property wrapper that holds a value tied to the lifetime of an in-progress gesture. Unlike @State, the value it stores is transient: SwiftUI tracks the gesture for you, and the moment the gesture ends or is cancelled it automatically resets the property to its initial value. Reach for it when you want a view to reflect the live, ephemeral state of a drag, press, or magnification — an offset that follows a finger, or a highlight that should vanish the instant the user lets go — without writing any teardown code yourself.

  1. Declare the transient value with @GestureState

    Apply the wrapper to a stored property and give it an initial value that represents the resting state. SwiftUI keeps the property equal to this value except while the gesture is active. In the example, @GestureState private var isPressed = false defines a Boolean that is false whenever no press is underway.

  2. Project the binding with the $ prefix

    The wrapper exposes a projected value through the $ syntax, which produces the handle a gesture needs to mutate the transient state. Here $isPressed is passed into the gesture so SwiftUI knows which GestureState property to drive as the interaction progresses.

  3. Drive it from a gesture with updating(_:body:)

    The updating(_:body:) modifier connects a gesture to a GestureState binding and runs a closure on every value change. The closure receives the gesture's current value, an inout state you write to, and a transaction. In the example, LongPressGesture(minimumDuration: 0.5).updating($isPressed) { current, state, _ in state = current } copies the press's progress into isPressed as long as the finger is held down.

  4. Rely on the automatic reset

    Because GestureState is owned by the gesture, you never reset it manually — when the gesture completes or is interrupted, SwiftUI restores the initial value. That is why isPressed snaps back to false on release with no extra code, returning the view to its idle appearance.

  5. Read it like any view state

    Inside body, the wrapped value is read directly to derive the UI, and changes flow through SwiftUI's normal update mechanism. The example reads isPressed to choose the fill (Color.green vs Color.blue), the scaleEffect, and the Text(isPressed ? "Pressed" : "Idle") label so the circle visibly responds while the press lasts.

Try it — Change the closure body from state = current to state = !current and watch the reset still kick in on release — the circle stays idle during the press and only the GestureState lifetime, not your logic, returns it to false.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

GestureState.swift
struct GestureStateDemo: View {
    @GestureState private var isPressed = false

    var body: some View {
        VStack(spacing: 16) {
            Text("Press and hold the circle")
                .font(.headline)
            Circle()
                .fill(isPressed ? Color.green : Color.blue)
                .frame(width: 100, height: 100)
                .scaleEffect(isPressed ? 1.2 : 1.0)
                .gesture(
                    LongPressGesture(minimumDuration: 0.5)
                        .updating($isPressed) { current, state, _ in
                            state = current
                        }
                )
            Text(isPressed ? "Pressed" : "Idle")
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
Press and hold the circle Idle
Press and hold the circle Idle
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →