The most comprehensive gesture library for SwiftUI
25+ gesture types β’ Shape recognition β’ Recording & playback β’ Full accessibility
| Category | Gestures |
|---|---|
| Basic | Tap, Double Tap, Triple Tap, Long Press (with progress) |
| Swipe | All directions, Multi-direction, Edge Swipe |
| Transform | Pinch, Rotation, Pan with velocity, Bounded Drag |
| Multi-finger | Two-finger tap, Three-finger tap, Two-finger drag |
| Advanced | Shape Recognition, Gesture Sequences, Force Touch, Hover |
| Motion | Shake Detection, Tilt (accelerometer-based) |
| Tools | Debugger Overlay, Recording/Playback, Haptic Integration |
Draw shapes and GestureKit recognizes them automatically:
Rectangle()
.onShapeDrawn { result in
print("Shape: \(result.shape)") // .circle, .square, .triangle...
print("Confidence: \(result.confidence)") // 0.0 - 1.0
}Supported Shapes: Circle, Square, Rectangle, Triangle, Line, Checkmark, Cross, Arrow, Star, Heart
Record user gestures and replay them for tutorials, demos, or testing:
@StateObject var recorder = GestureRecorder()
@StateObject var player = GesturePlayer()
// Record
Rectangle()
.gestureRecording(recorder: recorder)
// Playback
player.play(recording) { point, index in
// Animate based on playback
}dependencies: [
.package(url: "https://github.com/muhamm3t-cap/SwiftUI-Gesture-Library.git", from: "2.0.0")
]Or in Xcode: File β Add Package Dependencies β Enter the URL above.
import GestureKit
struct ContentView: View {
@State private var scale: CGFloat = 1.0
@State private var rotation: Angle = .zero
var body: some View {
Image("photo")
.resizable()
.scaledToFit()
// Swipe gestures
.onSwipe(.left) { print("Previous") }
.onSwipe(.right) { print("Next") }
// Double tap to reset
.onDoubleTap {
scale = 1.0
rotation = .zero
}
// Pinch to zoom
.onPinch { newScale in
scale = newScale
}
// Rotation
.onRotate { angle in
rotation = angle
}
.scaleEffect(scale)
.rotationEffect(rotation)
}
}// Tap variations
view.gestureKit(.tap) { /* action */ }
view.onDoubleTap { /* action */ }
view.onTripleTap { /* action */ }
// Long press with progress
view.onLongPress(
minimumDuration: 1.0,
onStart: { /* started pressing */ },
onProgress: { progress in /* 0.0 to 1.0 */ },
onComplete: { /* completed! */ }
)// Single direction
view.onSwipe(.left, minimumDistance: 50) { print("Swiped left") }
view.onSwipe(.right) { print("Swiped right") }
view.onSwipe(.up) { print("Swiped up") }
view.onSwipe(.down) { print("Swiped down") }
// All directions
view.onSwipeAny { direction in
print("Swiped \(direction.rawValue)")
}
// Edge swipe (like iOS back gesture)
view.onEdgeSwipe(.leading, threshold: 50) {
print("Swiped from leading edge")
}// Pinch to zoom
view.onPinch(
onChanged: { scale in /* during pinch */ },
onEnded: { finalScale in /* pinch ended */ }
)
// Rotation
view.onRotate(
onChanged: { angle in /* during rotation */ },
onEnded: { finalAngle in /* rotation ended */ }
)
// Pan with velocity
view.onPan(
onChanged: { translation, velocity in /* during pan */ },
onEnded: { translation, predictedEnd in /* pan ended */ }
)
// Bounded drag
@State var offset: CGSize = .zero
view.draggable(
offset: $offset,
bounds: CGRect(x: -100, y: -100, width: 200, height: 200)
)
// Combined transforms (pinch + rotate + drag)
@State var scale: CGFloat = 1.0
@State var rotation: Angle = .zero
@State var offset: CGSize = .zero
view.transformable(
scale: $scale,
rotation: $rotation,
offset: $offset,
minScale: 0.5,
maxScale: 3.0
)// Two-finger tap (iOS only)
view.onTwoFingerTap {
print("Two fingers tapped")
}// Shake detection
view.onShake {
print("Device shaken!")
}
// Tilt detection
view.onTilt { direction in
switch direction {
case .left: print("Tilted left")
case .right: print("Tilted right")
case .forward: print("Tilted forward")
case .backward: print("Tilted backward")
case .flat: print("Flat")
}
}view.onShapeDrawn(
lineWidth: 3,
lineColor: .blue,
onDrawing: { path in
// Track drawing in progress
print("Points: \(path.points.count)")
},
onComplete: { result in
print("Shape: \(result.shape)")
print("Confidence: \(result.confidence)")
print("Duration: \(result.drawDuration)s")
}
)GestureKit includes 12 animation presets for gesture feedback:
@State private var animateBounce = false
Button("Tap Me") {
animateBounce = true
}
.bounceAnimation(trigger: $animateBounce)| Preset | Description |
|---|---|
.bounce |
Scale down and spring back |
.shake |
Horizontal shake |
.pulse |
Scale up with opacity |
.wiggle |
Rotational wiggle |
.rubber |
Rubber band stretch |
.flip |
3D flip |
.glow |
Shadow glow effect |
.slide |
Horizontal slide |
.fade |
Opacity pulse |
.rotate |
Z-axis rotation |
.swing |
Pendulum swing |
.scale |
Scale up |
// Using presets
view.gestureAnimation(.bounce, trigger: $trigger)
view.gestureAnimation(.shake, trigger: $trigger)
view.gestureAnimation(.wiggle, trigger: $trigger)
// With configuration
view.bounceAnimation(
trigger: $trigger,
scale: 0.85,
config: .init(duration: 0.4, autoreverses: true)
)Built-in haptic feedback for all gestures:
// Enable/disable globally
GestureKit.hapticsEnabled = true
// Manual haptics
HapticEngine.impact(.light)
HapticEngine.impact(.medium)
HapticEngine.impact(.heavy)
HapticEngine.notification(.success)
HapticEngine.notification(.warning)
HapticEngine.notification(.error)
HapticEngine.selection()
// Custom patterns
HapticEngine.playPattern(.heartbeat)
HapticEngine.playPattern(.celebration)
HapticEngine.playPattern(.doubleTap)
// View modifiers
view.hapticOnTap(.medium)
view.hapticOnLongPress(.heavy)Visual debugging for gesture development:
ContentView()
.enableGestureDebug(true)
.gestureDebuggerOverlay(
position: .bottomTrailing,
showTouches: true,
touchColor: .blue
)Features:
- Real-time gesture event log
- Touch point visualization
- Gesture state tracking
- Export logs for analysis
GestureKit provides full accessibility support:
// Accessible gestures with alternatives
view.accessibleSwipe(.left, label: "Go back") {
navigateBack()
}
view.accessibleDoubleTap(label: "Zoom in") {
zoomIn()
}
view.accessibleLongPress(label: "Show options") {
showOptions()
}
// Respects reduced motion
view.accessibleAnimation(.spring(), value: isActive)
// VoiceOver announcements
VoiceOverAnnouncement.gestureCompleted("Swipe")
VoiceOverAnnouncement.shapeRecognized(.circle)Record gestures for tutorials, demos, or automated testing:
@StateObject var recorder = GestureRecorder()
@StateObject var player = GesturePlayer()
var body: some View {
VStack {
Canvas()
.gestureRecording(recorder: recorder, showIndicator: true)
.gesturePlayback(
player: player,
recording: selectedRecording,
pointColor: .blue
)
HStack {
Button("Record") {
recorder.startRecording()
}
Button("Stop") {
if let recording = recorder.stopRecording(name: "My Gesture") {
// Recording saved
}
}
Button("Play") {
player.play(selectedRecording, speed: 1.0) { point, index in
// Handle each point
} completion: {
// Playback complete
}
}
}
}
}Features:
- Automatic persistence (UserDefaults)
- Export/import recordings
- Playback speed control
- Progress tracking
- Visual playback overlay
Detect sequences of gestures:
@StateObject var tracker = GestureSequenceTracker()
view.onAppear {
tracker.track(
sequence: GestureSequence(
name: "Secret Code",
gestures: [.swipe(.up), .swipe(.down), .doubleTap],
timeLimit: 3.0
)
) {
print("Secret code entered!")
}
}Built-in sequences:
GestureSequence.doubleTapHoldGestureSequence.swipeAndTap
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GestureKit β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββββββ β
β β Core β β Advanced β β Tools β β
β β Gestures β β Gestures β β β β
β βββββββββββββββ€ βββββββββββββββ€ βββββββββββββββββββββββ€ β
β β β’ Tap β β β’ Shape β β β’ Debugger Overlay β β
β β β’ Swipe β β Recognitionβ β β’ Recorder/Player β β
β β β’ Pinch β β β’ Sequences β β β’ Accessibility β β
β β β’ Rotation β β β’ Shake β β β’ Haptic Engine β β
β β β’ Pan β β β’ Tilt β β β’ Animations β β
β β β’ Long Pressβ β β’ Multi- β β β β
β β β’ Edge Swipeβ β finger β β β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β View Extensions β
β .onSwipe() .onPinch() .onRotate() .onShapeDrawn() ... β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Platform | Minimum Version |
|---|---|
| iOS | 15.0+ |
| macOS | 13.0+ |
| tvOS | 15.0+ |
| watchOS | 8.0+ |
| visionOS | 1.0+ |
- Swift 5.9+
- Xcode 15.0+
Contributions are welcome! Please read our Contributing Guide first.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
GestureKit is available under the MIT license. See the LICENSE file for more info.
Made with β€οΈ for the SwiftUI community