TechnologiesSwiftUI

GestureStateGesture struct

iOSmacOStvOSwatchOSvisionOSiOS 13.0+✓ renders

A gesture that updates the state provided by a gesture's updating callback.

How it works

GestureStateGesture is the gesture type SwiftUI produces when you attach the updating(_:body:) modifier to another gesture. It pairs a base gesture with a @GestureState property, feeding live gesture values into that state while the gesture is active and automatically resetting the state to its initial value the instant the gesture ends. Reach for it whenever you want transient, in-flight feedback — a view that follows your finger and snaps back when you lift it — without writing the bookkeeping to clear that state yourself.

  1. Declare the transient state with @GestureState

    A @GestureState property holds a value that lives only for the duration of a gesture; SwiftUI restores it to its declared initial value as soon as the gesture finishes. Here @GestureState private var dragOffset = CGSize.zero is the storage that GestureStateGesture writes into, and CGSize.zero is the value it springs back to on release.

  2. Wrap a base gesture with updating(_:body:)

    Calling .updating(_:body:) on any Gesture returns a GestureStateGesture that decorates the original. The base gesture supplies the raw events while the modifier defines how each event maps into the bound state. In the example DragGesture().updating(...) turns a plain drag into a GestureStateGesture driving dragOffset.

  3. Bind the state projection with $

    The first argument to updating is a GestureState projected with the $ prefix, which is how GestureStateGesture knows which @GestureState to mutate. Passing $dragOffset hands the gesture write access to that transient property — and only for the gesture's lifetime.

  4. Compute the value inside the body closure

    The body closure receives the gesture's current value, an inout reference to the state, and a transaction. Assigning to state is the only way the bound property changes. The example's { value, state, _ in state = value.translation } copies the live value.translation into dragOffset on every drag update.

  5. Attach it like any Gesture and read the state

    GestureStateGesture conforms to Gesture, so it slots straight into .gesture(...) and the rest of your view reads the bound property normally. Here .offset(dragOffset) moves the Circle() as the value flows in, and because the state auto-resets, the circle returns to center the moment the drag ends.

Try it — Change the closure body from state = value.translation to state = CGSize(width: value.translation.width, height: 0) and the circle will track horizontally only — still snapping back to zero on release, showing how GestureStateGesture funnels just what you assign into the transient state.

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.

GestureStateGesture.swift
struct GestureStateGestureDemo: View {
    @GestureState private var dragOffset = CGSize.zero

    var body: some View {
        VStack(spacing: 16) {
            Text("Drag the circle")
                .font(.headline)
            Circle()
                .fill(.blue)
                .frame(width: 80, height: 80)
                .offset(dragOffset)
                .gesture(
                    DragGesture()
                        .updating($dragOffset) { value, state, _ in
                            state = value.translation
                        }
                )
            Text("Offset: \(Int(dragOffset.width)), \(Int(dragOffset.height))")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
Drag the circle Offset: {Int(dragOffset.width)}, {Int(dragOffset.height)}
Drag the circle Offset: {Int(dragOffset.width)}, {Int(dragOffset.height)}
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →