From b44e1839ca7bddae467ef3aa015f0b689d4b3167 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 12 Aug 2025 13:25:16 -0700 Subject: [PATCH] fix crash in adjustForMaintainVisibleContentPosition (#53208) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53208 changelog: [internal] When Fabric View Culling is enabled together with immediate state update, it may lead to a crash inside of `[RCTScrollViewComponentView _adjustForMaintainVisibleContentPosition]`. When doing immediate state update, we can avoid calling `[RCTScrollViewComponentView _adjustForMaintainVisibleContentPosition]` altogether to avoid the crash. Reviewed By: lenaic Differential Revision: D80000362 fbshipit-source-id: 123b70aa31edb14a99bb968648eb8b8aac84afb6 --- .../ScrollView/RCTScrollViewComponentView.mm | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index fc33f648bb6..671f3387d03 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -119,6 +119,10 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt // Once an accessibility API is used, view culling will be disabled for the entire session. BOOL _isAccessibilityAPIUsed; + // Flag to temporarily disable maintainVisibleContentPosition adjustments during immediate state updates + // to prevent conflicts between immediate content offset updates and visible content position logic + BOOL _avoidAdjustmentForMaintainVisibleContentPosition; + RCTVirtualViewContainerState *_virtualViewContainerState; } @@ -640,6 +644,11 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu return; } + BOOL enableImmediateUpdateModeForContentOffsetChanges = + ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(); + + _avoidAdjustmentForMaintainVisibleContentPosition = enableImmediateUpdateModeForContentOffsetChanges; + auto contentOffset = RCTPointFromCGPoint(_scrollView.contentOffset); BOOL isAccessibilityAPIUsed = _isAccessibilityAPIUsed; _state->updateState( @@ -655,9 +664,10 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu UIAccessibilityIsVoiceOverRunning() || UIAccessibilityIsSwitchControlRunning() || isAccessibilityAPIUsed; return std::make_shared(newData); }, - ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges() - ? EventQueue::UpdateMode::unstable_Immediate - : EventQueue::UpdateMode::Asynchronous); + enableImmediateUpdateModeForContentOffsetChanges ? EventQueue::UpdateMode::unstable_Immediate + : EventQueue::UpdateMode::Asynchronous); + + _avoidAdjustmentForMaintainVisibleContentPosition = NO; } - (void)prepareForRecycle @@ -1057,7 +1067,7 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu - (void)_adjustForMaintainVisibleContentPosition { const auto &props = static_cast(*_props); - if (!props.maintainVisibleContentPosition) { + if (!props.maintainVisibleContentPosition || _avoidAdjustmentForMaintainVisibleContentPosition) { return; }