Skip to content

Commit

Permalink
Restore scroll position when scroll view is hidden and shown
Browse files Browse the repository at this point in the history
Summary:
ScrollViews don't properly maintain position where they are hidden and shown. On iOS, when a UIScrollView (or its ancestor) is hidden, its scroll position is set to 0 (its window also becomes nil). When it is shown again, its scroll position is not restored.

When a scroll is attempted when the scroll view is hidden, we keep track of the last known offset before it was hidden. Then, in updateLayoutMetrics (which is triggered when the view is shown), we apply the pending offset if there is one. This is [consistent with Android's behavior in ReactScrollView.java](https://www.internalfb.com/code/fbsource/[2930f8c146af62ad63673c8d34e9876b77634c05]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java?lines=289).

Changelog:
[Internal][Fixed] - In onLayoutChange, only scroll if the view is shown and the content view is ready

Reviewed By: cipolleschi

Differential Revision: D42815359

fbshipit-source-id: 4b209c1e54edf3f5c0bea902b48450a1a2e9661a
  • Loading branch information
genkikondo authored and facebook-github-bot committed Jan 30, 2023
1 parent c000409 commit 47903d0
Showing 1 changed file with 23 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@

using namespace facebook::react;

struct PendingOffset {
bool isPending;
CGPoint offset;
CGPoint lastOffset;
};

static CGFloat const kClippingLeeway = 44.0;

static UIScrollViewKeyboardDismissMode RCTUIKeyboardDismissModeFromProps(ScrollViewProps const &props)
Expand Down Expand Up @@ -99,6 +105,8 @@ @implementation RCTScrollViewComponentView {
BOOL _shouldUpdateContentInsetAdjustmentBehavior;

CGPoint _contentOffsetWhenClipped;

PendingOffset _pendingOffset;
}

+ (RCTScrollViewComponentView *_Nullable)findScrollViewComponentViewForView:(UIView *)view
Expand Down Expand Up @@ -173,6 +181,12 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
_containerView.transform = transform;
_scrollView.transform = transform;
}

// If there is a pending offset, apply it
if (_pendingOffset.isPending) {
[self scrollTo:_pendingOffset.offset.x y:_pendingOffset.offset.y animated:false];
_pendingOffset.isPending = false;
}
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
Expand Down Expand Up @@ -421,6 +435,13 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
[self _updateStateWithContentOffset];
}

// If the view is hidden, then set as pending offset. Apply it later on
// updateLayoutMetrics.
if (_scrollView.window == nil && !_pendingOffset.isPending) {
_pendingOffset.offset = _pendingOffset.lastOffset;
_pendingOffset.isPending = true;
}

NSTimeInterval now = CACurrentMediaTime();
if ((_lastScrollEventDispatchTime == 0) || (now - _lastScrollEventDispatchTime > _scrollEventThrottle)) {
_lastScrollEventDispatchTime = now;
Expand All @@ -431,6 +452,8 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
RCTSendScrollEventForNativeAnimations_DEPRECATED(scrollView, self.tag);
}

_pendingOffset.lastOffset = _scrollView.contentOffset;

[self _remountChildrenIfNeeded];
}

Expand Down

0 comments on commit 47903d0

Please sign in to comment.