@@ -54,7 +54,14 @@ fileprivate extension DrawerPosition {
5454 ]
5555}
5656
57- let kVelocityTreshold : CGFloat = 0
57+ // Minimum velocity (pts/sec) to advance position when prediction is ambiguous
58+ let kVelocityTreshold : CGFloat = 800
59+
60+ // Minimum drag distance (ratio of distance between positions) to allow position change
61+ let kMinimumDragDistanceRatio : CGFloat = 0.25
62+
63+ // Time horizon (seconds) for projecting target position based on current velocity
64+ let kVelocityProjectionTime : CGFloat = 0.08
5865
5966// Vertical leeway is used to cover the bottom with springy animations.
6067let kVerticalLeeway : CGFloat = 10.0
@@ -263,8 +270,8 @@ private struct ChildScrollViewInfo {
263270 panGestureRecognizer. minimumNumberOfTouches = 1
264271 return panGestureRecognizer
265272 } ( )
266-
267- /// Damping ratio of the spring animation when opening or closing the drawer
273+
274+ /// Damping ratio of the spring animation when opening or closing the drawer
268275 public var animationSpringDampingRatio : CGFloat = 0.8
269276
270277 /// Boolean indicating if the activity drawer should dismiss when you scroll down
@@ -908,20 +915,45 @@ private struct ChildScrollViewInfo {
908915 // Let it scroll.
909916 log ( " Let child view scroll. " )
910917 } else if drawerPanStarted {
918+ log ( " drawerPanStarted " )
911919 self . delegate? . drawerWillEndDragging ? ( self )
912920
913- // Check velocity and snap position separately:
914- // 1) A treshold for velocity that makes drawer slide to the next state
915- // 2) A prediction that estimates the next position based on target offset.
916- // If 2 doesn't evaluate to the current position, use that.
917- let targetOffset = self . frame. origin. y + velocity. y / 100
921+ // Determine next position based on:
922+ // 1) Minimum drag distance - require meaningful movement before changing position
923+ // 2) Velocity threshold - fast swipes can advance to next position
924+ // 3) Position prediction - estimate target based on velocity and current offset
925+
926+ guard let superview = superview else {
927+ return
928+ }
929+
930+ // Measure actual drag distance from gesture start
931+ let dragDistance = abs ( self . frame. origin. y - self . panOrigin)
932+
933+ // Project target position based on current velocity
934+ let projectedDistance = velocity. y * kVelocityProjectionTime
935+ let targetOffset = self . frame. origin. y + projectedDistance
918936 let targetPosition = positionFor ( offset: targetOffset)
919937
920- // The positions are reversed, reverse the sign.
921- let advancement = velocity. y > 0 ? - 1 : 1
938+ // Check if drag meets minimum threshold for position change
939+ let hasMinimumDragDistance : Bool
940+ if targetPosition != self . position {
941+ let currentPositionSnapOffset = self . snapPosition ( for: self . position, inSuperView: superview)
942+ let targetPositionSnapOffset = self . snapPosition ( for: targetPosition, inSuperView: superview)
943+ let distanceBetweenPositions = abs ( targetPositionSnapOffset - currentPositionSnapOffset)
944+ let minimumDragDistance = distanceBetweenPositions * kMinimumDragDistanceRatio
945+ hasMinimumDragDistance = dragDistance >= minimumDragDistance
946+ } else {
947+ hasMinimumDragDistance = true
948+ }
922949
950+ // Determine next position: insufficient drag returns to current, high velocity
951+ // advances to next, otherwise use velocity-projected target
952+ let advancement = velocity. y > 0 ? - 1 : 1
923953 let nextPosition : DrawerPosition
924- if targetPosition == self . position && abs ( velocity. y) > kVelocityTreshold,
954+ if !hasMinimumDragDistance {
955+ nextPosition = self . position
956+ } else if targetPosition == self . position && abs ( velocity. y) > kVelocityTreshold,
925957 let advanced = self . snapPositionsDescending. advance ( from: targetPosition, offset: advancement) {
926958 nextPosition = advanced
927959 } else {
@@ -1368,9 +1400,34 @@ private struct ChildScrollViewInfo {
13681400extension DrawerView : UIGestureRecognizerDelegate {
13691401
13701402 override public func gestureRecognizerShouldBegin( _ gestureRecognizer: UIGestureRecognizer ) -> Bool {
1371- if gestureRecognizer === panGestureRecognizer || gestureRecognizer === overlayTapRecognizer {
1403+ if gestureRecognizer === panGestureRecognizer {
1404+ guard enabled else { return false }
1405+
1406+ // Check if the pan gesture is primarily horizontal (like swipe-to-delete)
1407+ // If so, don't begin to allow those gestures to work
1408+ let translation = panGestureRecognizer. translation ( in: self )
1409+ let velocity = panGestureRecognizer. velocity ( in: self )
1410+
1411+ // If there's meaningful translation/velocity, check direction
1412+ if abs ( translation. x) > 0 || abs ( translation. y) > 0 {
1413+ let isHorizontal = abs ( translation. x) > abs ( translation. y)
1414+ if isHorizontal {
1415+ return false
1416+ }
1417+ } else if abs ( velocity. x) > 0 || abs ( velocity. y) > 0 {
1418+ let isHorizontal = abs ( velocity. x) > abs ( velocity. y)
1419+ if isHorizontal {
1420+ return false
1421+ }
1422+ }
1423+
1424+ return true
1425+ }
1426+
1427+ if gestureRecognizer === overlayTapRecognizer {
13721428 return enabled
13731429 }
1430+
13741431 return true
13751432 }
13761433
0 commit comments