2323#import " RCTCustomPullToRefreshViewProtocol.h"
2424#import " RCTEnhancedScrollView.h"
2525#import " RCTFabricComponentsPlugins.h"
26+ #import " RCTScrollContentViewComponentView.h"
2627#import " RCTVirtualViewContainerState.h"
2728
2829using namespace facebook ::react;
@@ -449,9 +450,10 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
449450 } else if (contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Always) {
450451 scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
451452 }
453+ ((RCTEnhancedScrollView *)_scrollView).padsContentForSafeArea =
454+ contentInsetAdjustmentBehavior != ContentInsetAdjustmentBehavior::Never;
452455 _shouldUpdateContentInsetAdjustmentBehavior = NO ;
453456 }
454- scrollView.padsContentForSafeArea = newScrollViewProps.padsContentForSafeArea ;
455457
456458 MAP_SCROLL_VIEW_PROP (disableIntervalMomentum);
457459 MAP_SCROLL_VIEW_PROP (snapToInterval);
@@ -464,6 +466,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
464466
465467 // Must run after `super updateProps:` so it is based on the latest calculated view.
466468 ((RCTEnhancedScrollView *)_scrollView).inverted = [self isInverted ];
469+ [self _updateContentViewSafeAreaPadding ];
467470}
468471
469472- (void )updateState : (const State::Shared &)state oldState : (const State::Shared &)oldState
@@ -563,6 +566,7 @@ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childCompone
563566 [_containerView insertSubview: childComponentView atIndex: index];
564567 if (![childComponentView conformsToProtocol: @protocol (RCTCustomPullToRefreshViewProtocol)]) {
565568 _contentView = childComponentView;
569+ [self _updateContentViewSafeAreaPadding ];
566570 }
567571}
568572
@@ -867,6 +871,46 @@ - (void)didMoveToWindow
867871 // The view is being removed, ensure that the scroll end event is dispatched
868872 [self _handleScrollEndIfNeeded ];
869873 }
874+ [self _updateContentViewSafeAreaPadding ];
875+ }
876+
877+ - (void )safeAreaInsetsDidChange
878+ {
879+ [super safeAreaInsetsDidChange ];
880+ [self _updateContentViewSafeAreaPadding ];
881+ }
882+
883+ - (void )layoutSubviews
884+ {
885+ [super layoutSubviews ];
886+ // Moving or resizing the view changes its insets without `safeAreaInsetsDidChange` firing; that only reports
887+ // window-level changes.
888+ [self _updateContentViewSafeAreaPadding ];
889+ }
890+
891+ /*
892+ * Hands the content container the safe area to pad its content by: the pair of insets across the scrollable axis,
893+ * unless `contentInsetAdjustmentBehavior` is `never`. Along the scrollable axis UIKit turns the safe area into content
894+ * insets instead, so content scrolls under the system UI.
895+ */
896+ - (void )_updateContentViewSafeAreaPadding
897+ {
898+ if (![_contentView isKindOfClass: [RCTScrollContentViewComponentView class ]]) {
899+ return ;
900+ }
901+ const auto &props = static_cast <const ScrollViewProps &>(*_props);
902+ UIEdgeInsets padding = UIEdgeInsetsZero;
903+ if (props.contentInsetAdjustmentBehavior != ContentInsetAdjustmentBehavior::Never) {
904+ UIEdgeInsets insets = self.safeAreaInsets ;
905+ if (props.horizontal ) {
906+ padding.top = insets.top ;
907+ padding.bottom = insets.bottom ;
908+ } else {
909+ padding.left = insets.left ;
910+ padding.right = insets.right ;
911+ }
912+ }
913+ ((RCTScrollContentViewComponentView *)_contentView).safeAreaPadding = padding;
870914}
871915
872916- (void )_handleScrollEndIfNeeded
0 commit comments