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
This commit is contained in:
Samuel Susla
2025-08-12 13:25:16 -07:00
committed by Facebook GitHub Bot
parent 4ee326f52c
commit b44e1839ca
@@ -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<const ScrollViewShadowNode::ConcreteState::Data>(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<const ScrollViewProps &>(*_props);
if (!props.maintainVisibleContentPosition) {
if (!props.maintainVisibleContentPosition || _avoidAdjustmentForMaintainVisibleContentPosition) {
return;
}