From 28a65f438789c29309d6e7c58063a73ca721ef43 Mon Sep 17 00:00:00 2001 From: Dmitry Rykun Date: Wed, 27 Apr 2022 06:50:03 -0700 Subject: [PATCH] ScrollView fails when contentInsetAdjustmentBehavior is automatic Summary: Fix to https://github.com/reactwg/react-native-new-architecture/discussions/30 We have already applied this solution to both [pre-Fabric](https://www.internalfb.com/code/fbsource/[1da0f2e3164d6f87e8221e0f0462ae93fdc5cbdc]/xplat/js/react-native-github/React/Views/ScrollView/RCTScrollView.m?lines=361) and [Fabric](https://www.internalfb.com/code/fbsource/[1da0f2e3164d6f87e8221e0f0462ae93fdc5cbdc]/xplat/js/react-native-github/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm?lines=36) scroll views. However Fabric can reuse components, and this has to be reapplied at every reuse. Changelog [iOS][Fixed] - ScrollView's contentInsetAdjustmentBehavior is reset to Never at every reuse to avoid layout artifacts. Reviewed By: cipolleschi Differential Revision: D35965080 fbshipit-source-id: 3ac26cf304b608d09ae6c0f05588b664381551f2 --- .../ScrollView/RCTScrollViewComponentView.mm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 2d339aead9dc1a..c67865e1122630 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -97,6 +97,7 @@ @implementation RCTScrollViewComponentView { // This helps to only update state from `scrollViewDidScroll` in case // some other part of the system scrolls scroll view. BOOL _isUserTriggeredScrolling; + BOOL _shouldUpdateContentInsetAdjustmentBehavior; CGPoint _contentOffsetWhenClipped; } @@ -120,6 +121,7 @@ - (instancetype)initWithFrame:(CGRect)frame _scrollView.delaysContentTouches = NO; ((RCTEnhancedScrollView *)_scrollView).overridingDelegate = self; _isUserTriggeredScrolling = NO; + _shouldUpdateContentInsetAdjustmentBehavior = YES; [self addSubview:_scrollView]; _containerView = [[UIView alloc] initWithFrame:CGRectZero]; @@ -266,7 +268,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & } } - if (oldScrollViewProps.contentInsetAdjustmentBehavior != newScrollViewProps.contentInsetAdjustmentBehavior) { + if ((oldScrollViewProps.contentInsetAdjustmentBehavior != newScrollViewProps.contentInsetAdjustmentBehavior) || + _shouldUpdateContentInsetAdjustmentBehavior) { auto const contentInsetAdjustmentBehavior = newScrollViewProps.contentInsetAdjustmentBehavior; if (contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Never) { scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; @@ -277,6 +280,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & } else if (contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Always) { scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways; } + _shouldUpdateContentInsetAdjustmentBehavior = NO; } MAP_SCROLL_VIEW_PROP(disableIntervalMomentum); @@ -390,6 +394,11 @@ - (void)prepareForRecycle { const auto &props = *std::static_pointer_cast(_props); _scrollView.contentOffset = RCTCGPointFromPoint(props.contentOffset); + // We set the default behavior to "never" so that iOS + // doesn't do weird things to UIScrollView insets automatically + // and keeps it as an opt-in behavior. + _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + _shouldUpdateContentInsetAdjustmentBehavior = YES; _state.reset(); _isUserTriggeredScrolling = NO; [super prepareForRecycle];