Skip to content

Commit c365ead

Browse files
committed
Retain scroll view position while moving between states (#587)
Previously, the panel might not consistently keep its scroll content offset when moving from its most expanded state to another. Changes made in this commit: * Keep the content offset of tracking scroll view in the following cases. A panel is moved... 1. Outside of the tracking scroll view. 2. Inside of a navigation bar/toolbar over the tracking scroll view. * Stopped the scroll offset reset of the `stopScrollDeceleration` flag in the `panningEnd` method when the panel transitions from its most expanded state because there is no issue without the reset.
1 parent 5d02681 commit c365ead

File tree

1 file changed

+39
-7
lines changed

1 file changed

+39
-7
lines changed

Sources/Core.swift

+39-7
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ class Core: NSObject, UIGestureRecognizerDelegate {
6565

6666
// Scroll handling
6767
private var initialScrollOffset: CGPoint = .zero
68-
private var stopScrollDeceleration: Bool = false
6968
private var scrollBounce = false
7069
private var scrollIndictorVisible = false
7170
private var scrollBounceThreshold: CGFloat = -30.0
@@ -706,8 +705,10 @@ class Core: NSObject, UIGestureRecognizerDelegate {
706705
}
707706

708707
// Determine whether the panel's dragging should be projected onto the scroll content scrolling
709-
stopScrollDeceleration = 0 > layoutAdapter.offsetFromMostExpandedAnchor
710-
if stopScrollDeceleration {
708+
let stopScrollDeceleration = 0 > layoutAdapter.offsetFromMostExpandedAnchor
709+
os_log(msg, log: devLog, type: .debug, "panningEnd -- offsetFromMostExpandedAnchor = \(layoutAdapter.offsetFromMostExpandedAnchor)")
710+
711+
if stopScrollDeceleration, state != layoutAdapter.mostExpandedState {
711712
os_log(msg, log: devLog, type: .debug, "panningEnd -- will stop scrolling at initialScrollOffset = \(initialScrollOffset)")
712713
DispatchQueue.main.async { [weak self] in
713714
guard let self = self else { return }
@@ -791,11 +792,42 @@ class Core: NSObject, UIGestureRecognizerDelegate {
791792

792793
initialSurfaceLocation = layoutAdapter.surfaceLocation
793794
if state == layoutAdapter.mostExpandedState, let scrollView = scrollView {
794-
if surfaceView.grabberAreaContains(location) {
795+
let scrollFrame = scrollView.convert(scrollView.bounds, to: nil)
796+
let touchStartingPoint = surfaceView.convert(initialLocation, to: nil)
797+
798+
ifLabel: if surfaceView.grabberAreaContains(location) {
795799
initialScrollOffset = scrollView.contentOffset
796-
} else {
800+
} else if scrollFrame.contains(touchStartingPoint) {
797801
let pinningOffset = contentOffsetForPinning(of: scrollView)
798802

803+
// This code block handles the scenario where there's a navigation bar or toolbar
804+
// above the tracking scroll view with corresponding content insets set, and users
805+
// move the panel by interacting with these bars. One case of the scenario can be
806+
// tested with 'Show Navigation Controller' in Samples.app
807+
do {
808+
// Adjust the location by subtracting scrollView's origin to reference the frame
809+
// rectangle of the scroll view itself.
810+
let _location = scrollView.convert(location, from: surfaceView) - scrollView.bounds.origin
811+
812+
os_log(msg, log: devLog, type: .debug, "startInteraction -- location in scroll view = \(_location))")
813+
814+
// Keep the scroll content offset if the current touch position is inside its
815+
// content inset area.
816+
switch layoutAdapter.position {
817+
case .top, .left:
818+
let base = value(of: scrollView.bounds.size)
819+
if value(of: pinningOffset) + (base - value(of: _location)) < 0 {
820+
initialScrollOffset = scrollView.contentOffset
821+
break ifLabel
822+
}
823+
case .bottom, .right:
824+
if value(of: pinningOffset) + value(of: _location) < 0 {
825+
initialScrollOffset = scrollView.contentOffset
826+
break ifLabel
827+
}
828+
}
829+
}
830+
799831
// `initialScrollOffset` must be reset to the pinning offset because the value of `scrollView.contentOffset`,
800832
// for instance, is a value in [-30, 0) on a bottom positioned panel with `allowScrollPanGesture(of:condition:)`.
801833
// If it's not reset, the following logic to shift the surface frame will not work and then the scroll
@@ -814,6 +846,8 @@ class Core: NSObject, UIGestureRecognizerDelegate {
814846
offset = -offsetDiff
815847
}
816848
}
849+
} else {
850+
initialScrollOffset = scrollView.contentOffset
817851
}
818852
os_log(msg, log: devLog, type: .debug, "initial scroll offset -- \(initialScrollOffset)")
819853
}
@@ -916,8 +950,6 @@ class Core: NSObject, UIGestureRecognizerDelegate {
916950
os_log(msg, log: devLog, type: .debug, "finishAnimation -- scroll offset = \(scrollView.contentOffset)")
917951
}
918952

919-
stopScrollDeceleration = false
920-
921953
os_log(msg, log: devLog, type: .debug, """
922954
finishAnimation -- state = \(state) \
923955
surface location = \(layoutAdapter.surfaceLocation) \

0 commit comments

Comments
 (0)