@@ -26,76 +26,65 @@ public struct HStackSnap<Content: View>: View {
2626 for pref in preferences {
2727
2828 itemFrames [ pref. id] = pref
29- // print(pref.rect.minX)
3029 }
3130 } )
32- . coordinateSpace ( name: ContentPreferenceKey . coordinateSpace)
3331 . disabled ( true )
34- . gesture (
35-
36- DragGesture ( )
37- . onChanged { gesture in
38-
39- self . scrollOffset = gesture. translation. width + prevScrollOffset
40-
41- } . onEnded { event in
42-
43- // scrollOffset += event.translation.width
44- // dragOffset = 0
32+ . gesture ( snapDrag)
33+ }
34+ . coordinateSpace ( name: ContentPreferenceKey . coordinateSpace)
35+ }
4536
46- guard var closestFrame : ContentPreferenceData = itemFrames . first ? . value else { return }
37+ // MARK: Internal
4738
48- func distanceToFrame ( x : CGFloat , absolute : Bool ) -> CGFloat {
39+ var content : ( ) -> Content
4940
50- if absolute {
51- return abs ( targetOffset - x)
52- } else {
53- return targetOffset - x
54- }
55- }
41+ var snapDrag : some Gesture {
5642
57- for (key, value) in itemFrames {
43+ DragGesture ( )
44+ . onChanged { gesture in
5845
59- let currDistance = distanceToFrame (
60- x: closestFrame. rect. minX,
61- absolute: true )
62- let newDistance = distanceToFrame ( x: value. rect. minX, absolute: true )
46+ self . scrollOffset = gesture. translation. width + prevScrollOffset
6347
64- print ( " ~~ \( value . rect . maxX ) " )
48+ } . onEnded { event in
6549
66- if newDistance < currDistance {
50+ guard var closestFrame : ContentPreferenceData = itemFrames . first ? . value else { return }
6751
68- closestFrame = value
69- }
70- }
52+ for (_, value) in itemFrames {
7153
72- withAnimation {
54+ let currDistance = distanceToTarget (
55+ x: closestFrame. rect. minX)
56+ let newDistance = distanceToTarget ( x: value. rect. minX)
7357
74- print ( distanceToFrame ( x : closestFrame . rect . minX , absolute : false ) )
58+ if abs ( newDistance ) < abs ( currDistance ) {
7559
76- scrollOffset += distanceToFrame (
77- x: closestFrame. rect. minX,
78- absolute: false )
79- }
60+ closestFrame = value
61+ }
62+ }
8063
81- } )
82- . onTapGesture {
64+ withAnimation ( . easeOut( duration: 0.2 ) ) {
8365
84- scrollOffset = 0
85- prevScrollOffset = 0
66+ scrollOffset += distanceToTarget (
67+ x: closestFrame. rect. minX)
68+ }
69+
70+ prevScrollOffset = scrollOffset
8671 }
87- }
8872 }
8973
90- // MARK: Internal
74+ func distanceToTarget ( x : CGFloat ) -> CGFloat {
9175
92- var content : ( ) -> Content
76+ return targetOffset - x
77+ }
9378
9479 // MARK: Private
9580
81+ /// Current scroll offset.
9682 @State private var scrollOffset : CGFloat = 0
83+
84+ /// Stored offset of previous scroll, so scroll state is resumed between drags.
9785 @State private var prevScrollOffset : CGFloat = 0
98-
86+
87+ ///
9988 @State private var targetOffset : CGFloat = 0
10089
10190 @State private var itemFrames : [ UUID : ContentPreferenceData ] = [ : ]
0 commit comments