SwiftUI Gestures (iOS 26+)
Review, write, and fix SwiftUI gesture interactions. Apply modern gesture APIs with correct composition, state management, and conflict resolution using Swift 6.3 patterns.
Scope boundary: This skill owns SwiftUI gesture recognition, composition,
gesture state, and gesture-specific accessibility alternatives. Broader
SwiftUI architecture/state ownership belongs in swiftui-patterns; list,
scroll, form, and control layout belongs in swiftui-layout-components; broad
UIKit bridging belongs in swiftui-uikit-interop.
When correcting Apple API availability, deprecation, or behavior claims, cite the relevant Sosumi or official Apple documentation URL in the response.
Contents
- Gesture Overview
- TapGesture
- LongPressGesture
- DragGesture
- MagnifyGesture (iOS 17+)
- RotateGesture (iOS 17+)
- Gesture Composition
@GestureState- Adding Gestures to Views
- Custom Gesture Protocol
- Common Mistakes
- Review Checklist
- References
Gesture Overview
Discrete gestures fire once (.onEnded). Continuous gestures stream
updates (.onChanged, .onEnded, .updating).
TapGesture
Recognizes one or more taps. Use the count parameter for multi-tap.
LongPressGesture
Succeeds after the user holds for minimumDuration. Fails if finger moves
beyond maximumDistance.
With visual feedback via @GestureState + .updating():
Shorthand: .onLongPressGesture(minimumDuration:perform:onPressingChanged:).
DragGesture
Tracks finger movement. Value provides startLocation, location,
translation, velocity, and predictedEndTranslation.
DragGesture.Value.velocity is available with DragGesture from iOS 13+;
do not confuse it with iOS 17+ gesture types such as MagnifyGesture and
RotateGesture.
Configure minimum distance and coordinate space:
MagnifyGesture (iOS 17+)
Replaces the deprecated MagnificationGesture. Tracks pinch-to-zoom scale.
RotateGesture (iOS 17+)
RotateGesture is the newer alternative to RotationGesture. Tracks two-finger rotation angle.
For persisted, clamped magnification and combined rotation examples, load references/gesture-patterns.md [blocked].
Gesture Composition
.simultaneously(with:) — both gestures recognized at the same time
The value is SimultaneousGesture.Value with .first and .second optionals.
.sequenced(before:) — first must succeed before second begins
.exclusively(before:) — only one succeeds (first has priority)
@GestureState
@GestureState is a property wrapper that automatically resets to its
initial value when the gesture ends. Use for transient feedback; use @State
for values that persist.
Custom reset with animation: @GestureState(resetTransaction: Transaction(animation: .spring))
Adding Gestures to Views
Three modifiers control gesture priority in the view hierarchy:
Use .gesture(parentTap) for the default lower-precedence parent gesture, or
.highPriorityGesture(parentTap) when the added parent gesture should win.
GestureMask
Control which gestures participate when using .gesture(_:including:):
Custom Gesture Protocol
Create reusable gestures by conforming to Gesture:
Wrap in a View extension for ergonomic API:
Common Mistakes
1. Misreading parent/child gesture precedence
Do not assume a parent .gesture() overrides child gestures. Choose the
relationship explicitly as shown in Adding Gestures to Views.
2. Using @State instead of @GestureState for transient state
Use @GestureState for values that should reset when recognition ends; keep
persistent results in @State. See @GestureState.
3. Not using .updating() for intermediate feedback
4. Using deprecated gesture types on iOS 17+
5. Heavy computation in onChanged
6. Using onTapGesture for actions that should be a Button
Reserve onTapGesture for multi-tap (count: 2+), tap-location-dependent
behavior, or adding tap recognition to non-interactive content that already
has appropriate accessibility traits.
Review Checklist
- Correct gesture type:
MagnifyGesture/RotateGesture(not deprecatedMagnification/Rotationvariants) -
@GestureStateused for transient values that should reset;@Statefor persisted values -
.updating()provides intermediate visual feedback during continuous gestures - Parent/child conflicts resolved with
.highPriorityGesture()or.simultaneousGesture() -
onChangedclosures are lightweight — no heavy computation every frame - Composed gestures use correct combinator:
simultaneously,sequenced, orexclusively - Persisted scale/rotation clamped to reasonable bounds in
onEnded - Custom
Gestureconformances return a gesture body; useAnyGesture<Value>when mapping to a customValue - Gesture-driven animations use
.springor similar for natural deceleration -
GestureMaskconsidered when mixing gestures across view hierarchy levels -
onTapGestureonly used wherecount > 1, tap location, or coordinate space matters — plain single-tap actions useButtoninstead
References
- Read references/gesture-patterns.md [blocked] when the task needs full drag-to-reorder, pinch-to-zoom, combined rotate+scale, velocity/projection, sequenced gesture state-machine, or gesture-specific UIKit interop examples.
- Gesture protocol
- TapGesture
- LongPressGesture
- DragGesture
- DragGesture.Value.velocity
- MagnifyGesture
- RotateGesture
- GestureState
- Composing SwiftUI gestures
- Adding interactivity with gestures


