Skills Plugins MCP Prompt Model 博客 我的中心

swiftui-gestures

Implement, review, or improve SwiftUI gesture handling. Use when adding tap, long press, drag, magnify, or rotate gestures, composing gestures with simultaneously/sequenced/exclusively, managing transient state with @GestureState, resolving parent/child gesture conflicts with highPriorityGesture or simultaneousGesture, building custom Gesture protocol conformances, or migrating from deprecated MagnificationGesture to MagnifyGesture or using the newer RotateGesture.

DeepseekModel Curated skill Quality Excellent · 90 v1.0.0

Get

https://deepseekmodel.com/api/download.php?id=dpearson2699-swift-ios-skills-skills-swiftui-gestures-skill-md&format=skill
Download .skill Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name swiftui-gestures description Implement, review, or improve SwiftUI gesture handling. Use when adding tap, long press, drag, magnify, or rotate gestures, composing gestures with simultaneously/sequenced/exclusively, managing transient state with @GestureState, resolving parent/child gesture conflicts with highPriorityGesture or simultaneousGesture, building custom Gesture protocol conformances, or migrating from deprecated MagnificationGesture to MagnifyGesture or using the newer RotateGesture. 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 Gesture Type Value Since TapGesture Discrete Void iOS 13 LongPressGesture Discrete Bool iOS 13 DragGesture Continuous DragGesture.Value iOS 13 MagnifyGesture Continuous MagnifyGesture.Value iOS 17 RotateGesture Continuous RotateGesture.Value iOS 17 SpatialTapGesture Discrete SpatialTapGesture.Value iOS 16 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. // Single, double, and triple tap TapGesture () .onEnded { tapped.toggle() } TapGesture (count: 2 ) .onEnded { handleDoubleTap() } TapGesture (count: 3 ) .onEnded { handleTripleTap() } // Shorthand modifier Text ( "Tap me" ).onTapGesture(count: 2 ) { handleDoubleTap() } LongPressGesture Succeeds after the user holds for minimumDuration . Fails if finger moves beyond maximumDistance . // Basic long press (0.5s default) LongPressGesture () .onEnded { _ in showMenu = true } // Custom duration and distance tolerance LongPressGesture (minimumDuration: 1.0 , maximumDistance: 10 ) .onEnded { _ in triggerHaptic() } With visual feedback via @GestureState + .updating() : @GestureState private var isPressing = false Circle () .fill(isPressing ? .red : .blue) .scaleEffect(isPressing ? 1.2 : 1.0 ) .gesture( LongPressGesture (minimumDuration: 0.8 ) .updating( $isPressing ) { current, state, _ in state = current } .onEnded { _ in completedLongPress = true } ) 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 . @State private var offset = CGSize .zero RoundedRectangle (cornerRadius: 16 ) .fill(.blue) .frame(width: 100 , height: 100 ) .offset(offset) .gesture( DragGesture () .onChanged { value in offset = value.translation } .onEnded { _ in withAnimation(.spring) { offset = .zero } } ) Configure minimum distance and coordinate space: DragGesture (minimumDistance: 20 , coordinateSpace: .global) MagnifyGesture (iOS 17+) Replaces the deprecated MagnificationGesture . Tracks pinch-to-zoom scale. @GestureState private var magnifyBy = 1.0 Image ( "photo" ) .resizable().scaledToFit() .scaleEffect(magnifyBy) .gesture( MagnifyGesture () .updating( $magnifyBy ) { value, state, _ in state = value.magnification } ) RotateGesture (iOS 17+) RotateGesture is the newer alternative to RotationGesture . Tracks two-finger rotation angle. @State private var angle = Angle .zero Rectangle () .fill(.blue).frame(width: 200 , height: 200 ) .rotationEffect(angle) .gesture( RotateGesture (minimumAngleDelta: .degrees( 1 )) .onChanged { value in angle = value.rotation } ) For persisted, clamped magnification and combined rotation examples, load references/gesture-patterns.md . Gesture Composition .simultaneously(with:) — both gestures recognized at the same time let magnify = MagnifyGesture () .onChanged { value in scale = value.magnification } let rotate = RotateGesture () .onChanged { value in angle = value.rotation } Image ( "photo" ) .scaleEffect(scale) .rotationEffect(angle) .gesture(magnify.simultaneously(with: rotate)) The value is SimultaneousGesture.Value with .first and .second optionals. .sequenced(before:) — first must succeed before second begins let longPressBeforeDrag = LongPressGesture (minimumDuration: 0.5 ) .sequenced(before: DragGesture ()) .onEnded { value in guard case .second( true , let drag ? ) = value else { return } finalOffset.width += drag.translation.width finalOffset.height += drag.translation.height } .exclusively(before:) — only one succeeds (first has priority) let doubleTapOrLongPress = TapGesture (count: 2 ) .exclusively(before: LongPressGesture () ) .onEnded { result in switch result { case .first( _ ): handleDoubleTap() case .second( _ ): handleLongPress() } } @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. @GestureState private var dragOffset = CGSize .zero // resets to .zero @State private var position = CGSize .zero // persists Circle () .offset( x: position.width + dragOffset.width, y: position.height + dragOffset.height ) .gesture( DragGesture () .updating( $dragOffset ) { value, state, _ in state = value.translation } .onEnded { value in position.width += value.translation.width position.height += value.translation.height } ) Custom reset with animation: @GestureState(resetTransaction: Transaction(animation: .spring)) Adding Gestures to Views Three modifiers control gesture priority in the view hierarchy: Modifier Behavior .gesture() Lower precedence than gestures already defined by the view or its children. .highPriorityGesture() Added gesture takes precedence over existing gestures. .simultaneousGesture() Added gesture processes at the same priority as existing gestures. let parentTap = TapGesture ().onEnded { handleParent() } VStack { Image (systemName: "star.fill" ) .onTapGesture { handleChild() } } .simultaneousGesture(parentTap) // Both handlers run on child content. 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:) : .gesture(drag, including: .gesture) // added gesture; disables subview gestures .gesture(drag, including: .subviews) // subview gestures; disables added gesture .gesture(drag, including: .all) // default: added + subview gestures .gesture(drag, including: .none) // disables added + subview gestures Custom Gesture Protocol Create reusable gestures by conforming to Gesture : struct SwipeGesture : Gesture { enum Direction { case left, right, up, down } typealias Value = Direction let minimumDistance: CGFloat init ( minimumDistance : CGFloat = 50 ) { self .minimumDistance = minimumDistance } var body: AnyGesture < Direction > { AnyGesture ( DragGesture (minimumDistance: minimumDistance) .map { value in let h = value.translation.width, v = value.translation.height if abs (h) > abs (v) { return h > 0 ? .right : .left } else { return v > 0 ? .down : .up } } ) } } // Usage Rectangle ().gesture( SwipeGesture ().onEnded { print ( "Swiped \( $0 ) " ) }) Wrap in a View extension for ergonomic API: extension View { func onSwipe ( perform action : @escaping ( SwipeGesture . Direction ) -> Void ) -> some View { gesture( SwipeGesture ().onEnded(action)) } } 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 // DON'T: No visual feedback during long press LongPressGesture (minimumDuration: 2.0 ) .onEnded { _ in showResult = true } // DO: Provide feedback while pressing @GestureState private var isPressing = false LongPressGesture (minimumDuration: 2.0 ) .updating( $isPressing ) { current, state, _ in state = current } .onEnded { _ in showResult = true } 4. Using deprecated gesture types on iOS 17+ // DON'T: Deprecated since iOS 17 MagnificationGesture () // deprecated — use MagnifyGesture() // DO: Use newer gesture types MagnifyGesture () // iOS 17+ RotateGesture () // iOS 17+ (newer alternative to RotationGesture) 5. Heavy computation in onChanged // DON'T: Expensive work called every frame (~60-120 Hz) DragGesture () .onChanged { value in let result = performExpensiveHitTest(at: value.location) let filtered = applyComplexFilter(result) updateModel(filtered) } // DO: Throttle or defer expensive work DragGesture () .onChanged { value in dragPosition = value.location // lightweight state update only } .onEnded { value in performExpensiveHitTest(at: value.location) // once at end } 6. Using onTapGesture for actions that should be a Button // DON'T: onTapGesture has no accessibility traits, VoiceOver role, // Voice Control targeting, Switch Control scanning, or keyboard activation Text ( "Delete" ) .onTapGesture { deleteItem() } // DO: Button provides all of these automatically Button ( "Delete" , role: .destructive) { deleteItem() } // DO: For custom visuals, use ButtonStyle instead of onTapGesture Button { toggleExpanded() } label: { CardView () } .buttonStyle(.plain) 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 deprecated Magnification / Rotation variants) @GestureState used for transient values that should reset; @State for persisted values .updating() provides intermediate visual feedback during continuous gestures Parent/child conflicts resolved with .highPriorityGesture() or .simultaneousGesture() onChanged closures are lightweight — no heavy computation every frame Composed gestures use correct combinator: simultaneously , sequenced , or exclusively Persisted scale/rotation clamped to reasonable bounds in onEnded Custom Gesture conformances return a gesture body; use AnyGesture<Value> when mapping to a custom Value Gesture-driven animations use .spring or similar for natural deceleration GestureMask considered when mixing gestures across view hierarchy levels onTapGesture only used where count > 1 , tap location, or coordinate space matters — plain single-tap actions use Button instead References Read references/gesture-patterns.md 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
Keywords that activate this skill. Click one to copy it.

This skill does not provide trigger words.

The downloaded .skill package contains the following fields.
Field Description
formatFormat tag (skill/v1)
skill_idUnique skill ID
nameSkill name
versionVersion
descriptionDescription
categoryCategories (array)
trigger_wordsTrigger words
tagsTags
sourceSource
source_urlSource URL (this page)
exported_atExported at (set per download)
system_promptSystem prompt body
model_configModel config: provider / model / temperature / max_tokens / top_p
examplesExamples
install_guideImport guide for Coze / Dify / Claude / custom frameworks
The same skill can be exported in different platform formats.
.skill Standard format with system_prompt and model_config, ready for any agent framework Download
.skillpro Enhanced format with scripts, tools, dependencies and hooks Download
.json Plain JSON export with system_prompt and model parameters only Download
Coze Markdown with frontmatter, for Coze platform import Download
Dify Dify DSL, import directly after creating an app Download

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

验证码 --

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。