Skip to content

Commit c495222

Browse files
committed
Add DragGesture and the long press gesture
1 parent 1f889b5 commit c495222

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//
2+
// Gesture.swift
3+
// AndroidSwiftUICore
4+
//
5+
// Continuous gestures. A drag reports through the fixed string callback rather
6+
// than growing the bridge with a two-number entry point: the interpreter sends
7+
// "<phase>;<startX>,<startY>;<x>,<y>" in points, which rebuilds a Value here.
8+
//
9+
10+
import Foundation
11+
12+
/// A gesture recognized on a view. `.gesture(_:)` currently accepts `DragGesture`.
13+
public protocol Gesture {}
14+
15+
public struct DragGesture: Gesture {
16+
17+
/// The state of a drag as it is recognized.
18+
public struct Value {
19+
public var startLocation: CGPoint
20+
public var location: CGPoint
21+
public var translation: CGSize
22+
}
23+
24+
/// How far the touch must move before the drag is recognized.
25+
public var minimumDistance: Double
26+
27+
internal var changedAction: ((Value) -> Void)?
28+
internal var endedAction: ((Value) -> Void)?
29+
30+
public init(minimumDistance: Double = 10) {
31+
self.minimumDistance = minimumDistance
32+
}
33+
34+
public func onChanged(_ action: @escaping (Value) -> Void) -> DragGesture {
35+
var copy = self
36+
copy.changedAction = action
37+
return copy
38+
}
39+
40+
public func onEnded(_ action: @escaping (Value) -> Void) -> DragGesture {
41+
var copy = self
42+
copy.endedAction = action
43+
return copy
44+
}
45+
}
46+
47+
internal extension DragGesture.Value {
48+
49+
/// Rebuilds a value from the interpreter's `"<phase>;<sx>,<sy>;<x>,<y>"`.
50+
init?(payload: String) {
51+
let fields = payload.split(separator: ";")
52+
guard fields.count == 3 else { return nil }
53+
let start = fields[1].split(separator: ",").compactMap { Double($0) }
54+
let current = fields[2].split(separator: ",").compactMap { Double($0) }
55+
guard start.count == 2, current.count == 2 else { return nil }
56+
self.startLocation = CGPoint(x: start[0], y: start[1])
57+
self.location = CGPoint(x: current[0], y: current[1])
58+
self.translation = CGSize(width: current[0] - start[0], height: current[1] - start[1])
59+
}
60+
}
61+
62+
// MARK: - Modifiers
63+
64+
public struct _GestureModifier: RenderModifier, _CallbackModifier {
65+
66+
let gesture: DragGesture
67+
68+
public var _modifierNode: ModifierNode { ModifierNode(kind: "drag") }
69+
70+
public func _callbackNode(in context: ResolveContext) -> ModifierNode {
71+
let changed = gesture.changedAction
72+
let ended = gesture.endedAction
73+
let id = context.callbacks.register(.string { payload in
74+
guard let value = DragGesture.Value(payload: payload) else { return }
75+
if payload.hasPrefix("ended") {
76+
ended?(value)
77+
} else {
78+
changed?(value)
79+
}
80+
})
81+
return ModifierNode(kind: "drag", args: [
82+
"action": .int(Int(id)),
83+
"minimumDistance": .double(gesture.minimumDistance),
84+
])
85+
}
86+
}
87+
88+
public struct _LongPressModifier: RenderModifier, _CallbackModifier {
89+
90+
let action: () -> Void
91+
92+
public var _modifierNode: ModifierNode { ModifierNode(kind: "longPress") }
93+
94+
public func _callbackNode(in context: ResolveContext) -> ModifierNode {
95+
let id = context.callbacks.register(.void(action))
96+
return ModifierNode(kind: "longPress", args: ["action": .int(Int(id))])
97+
}
98+
}
99+
100+
public extension View {
101+
102+
func gesture(_ gesture: DragGesture) -> ModifiedContent<Self, _GestureModifier> {
103+
modifier(_GestureModifier(gesture: gesture))
104+
}
105+
106+
func onLongPressGesture(perform action: @escaping () -> Void) -> ModifiedContent<Self, _LongPressModifier> {
107+
modifier(_LongPressModifier(action: action))
108+
}
109+
}

0 commit comments

Comments
 (0)