-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathButtonLocationTracking.swift
More file actions
68 lines (60 loc) · 2.54 KB
/
ButtonLocationTracking.swift
File metadata and controls
68 lines (60 loc) · 2.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import SwiftUI
/// Preference key for tracking button locations in a coordinate space
struct ButtonLocationPreferenceKey: PreferenceKey {
static var defaultValue: [CGRect] = []
static func reduce(value: inout [CGRect], nextValue: () -> [CGRect]) {
value.append(contentsOf: nextValue())
}
}
/// Modifier that tracks a view's location in a specified coordinate space
private struct ButtonLocationModifier: ViewModifier {
/// The name of the coordinate space to track the view in
let coordinateSpace: String
/// Callback when the view's location changes
let onLocationChanged: (CGRect) -> Void
init(coordinateSpace: String, onLocationChanged: @escaping (CGRect) -> Void) {
self.coordinateSpace = coordinateSpace
self.onLocationChanged = onLocationChanged
}
func body(content: Content) -> some View {
content
.background(
GeometryReader { geometry in
Color.clear
.preference(
key: ButtonLocationPreferenceKey.self,
value: [geometry.frame(in: .named(coordinateSpace))]
)
.onPreferenceChange(ButtonLocationPreferenceKey.self) { frames in
if let frame = frames.first {
onLocationChanged(frame)
}
}
}
)
}
}
public extension View {
/// Tracks the location of a view in a specified coordinate space
/// - Parameters:
/// - coordinateSpace: The name of the coordinate space to track the view in
/// - onLocationChanged: Callback when the view's location changes
/// - Returns: A view that tracks its location in the specified coordinate space
func trackButtonLocation(
in coordinateSpace: String,
onLocationChanged: @escaping (CGRect) -> Void
) -> some View {
modifier(
ButtonLocationModifier(
coordinateSpace: coordinateSpace,
onLocationChanged: onLocationChanged
)
)
}
/// Tracks the location of a view in the default "dragSpace" coordinate space
/// - Parameter onLocationChanged: Callback when the view's location changes
/// - Returns: A view that tracks its location in the drag space
func trackButtonLocation(onLocationChanged: @escaping (CGRect) -> Void) -> some View {
trackButtonLocation(in: "dragSpace", onLocationChanged: onLocationChanged)
}
}