Skip to content

Commit 51db0e4

Browse files
aljadanmeta-codesync[bot]
authored andcommitted
fix(iOS): reset zoomScale before contentOffset when recycling ScrollView (#58932)
Summary: In RTL, a recycled horizontal `ScrollView` starts scrolled to the end instead of the start. The first mount is fine, but every remount (unmount/mount, navigating back and forth) opens at the end. The cause is the reset order in `prepareForRecycle`. In RTL the scroll view is mirrored with `scaleX(-1)`, so UIKit reports `zoomScale == -1`. We reset `contentOffset` to 0 first, then set `zoomScale = 1.0`. UIKit treats that as a real zoom change and moves `contentOffset` to the max offset. The view is mirrored again on reuse, so it opens at the logical end. JS doesn't get an `onScroll`, so it still thinks the offset is 0. This regressed in 0.84 with the zoom reset (#54668). #55804 fixed the container frame but not the offset. It only shows when the content is wider than the scroll view. The fix is to reset `zoomScale` before `contentOffset`, so the offset reset runs last. ## Changelog: [IOS] [FIXED] - Fix recycled RTL horizontal ScrollView starting at the end instead of the start Pull Request resolved: #58932 Test Plan: Repro: https://github.com/aljadan/scrollView-remount-repro | Current behavior | After the fix | |--------|--------| | https://github.com/user-attachments/assets/83339700-4e77-4ef4-9537-893731a18e00 | https://github.com/user-attachments/assets/bd0a1a17-c69f-4af9-8128-988c55cc560d | Reviewed By: christophpurrer Differential Revision: D124744712 Pulled By: javache fbshipit-source-id: 324234b07f638a1ee3315d292d5b0df415835d2e
1 parent d2669f4 commit 51db0e4

1 file changed

Lines changed: 4 additions & 2 deletions

File tree

‎packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,11 @@ - (void)prepareForRecycle
692692
_state.reset();
693693

694694
const auto &props = static_cast<const ScrollViewProps &>(*_props);
695-
_scrollView.contentOffset = RCTCGPointFromPoint(props.contentOffset);
696-
// Reset zoom scale to default
695+
// Reset zoom scale to default. This must happen before resetting contentOffset:
696+
// in RTL the mirroring transform makes UIKit report zoomScale == -1, and changing
697+
// it to 1 makes UIKit move contentOffset.
697698
_scrollView.zoomScale = 1.0;
699+
_scrollView.contentOffset = RCTCGPointFromPoint(props.contentOffset);
698700
// Invalidate cached content size so that updateState: recalculates the
699701
// container frame after zoomScale reset (which may have mutated it in RTL).
700702
_contentSize = CGSizeZero;

0 commit comments

Comments
 (0)