diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 0c357ad39e6456..e497288b1b7d92 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -171,6 +171,12 @@ type IOSProps = $ReadOnly<{| * @platform ios */ automaticallyAdjustContentInsets?: ?boolean, + /** + * Controls whether iOS should automatically adjust the scroll indicator + * insets. The default value is true. Available on iOS 13 and later. + * @platform ios + */ + automaticallyAdjustsScrollIndicatorInsets?: ?boolean, /** * The amount by which the scroll view content is inset from the edges * of the scroll view. Defaults to `{top: 0, left: 0, bottom: 0, right: 0}`. diff --git a/Libraries/Components/ScrollView/ScrollViewNativeComponent.js b/Libraries/Components/ScrollView/ScrollViewNativeComponent.js index f5433a0b25e21c..cdfc8ff450f3c3 100644 --- a/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +++ b/Libraries/Components/ScrollView/ScrollViewNativeComponent.js @@ -26,6 +26,7 @@ const ScrollViewNativeComponent: HostComponent = NativeComponentRegistry. alwaysBounceHorizontal: true, alwaysBounceVertical: true, automaticallyAdjustContentInsets: true, + automaticallyAdjustsScrollIndicatorInsets: true, bounces: true, bouncesZoom: true, canCancelContentTouches: true, diff --git a/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js b/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js index 405667d1f38059..24e0c70f442787 100644 --- a/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js +++ b/Libraries/Components/ScrollView/ScrollViewNativeComponentType.js @@ -21,6 +21,7 @@ export type ScrollViewNativeProps = $ReadOnly<{ alwaysBounceHorizontal?: ?boolean, alwaysBounceVertical?: ?boolean, automaticallyAdjustContentInsets?: ?boolean, + automaticallyAdjustsScrollIndicatorInsets?: ?boolean, bounces?: ?boolean, bouncesZoom?: ?boolean, canCancelContentTouches?: ?boolean, diff --git a/Libraries/Components/ScrollView/ScrollViewViewConfig.js b/Libraries/Components/ScrollView/ScrollViewViewConfig.js index 19818e0fae8440..5b276d54fa0fe0 100644 --- a/Libraries/Components/ScrollView/ScrollViewViewConfig.js +++ b/Libraries/Components/ScrollView/ScrollViewViewConfig.js @@ -24,6 +24,7 @@ const ScrollViewViewConfig = { alwaysBounceHorizontal: true, alwaysBounceVertical: true, automaticallyAdjustContentInsets: true, + automaticallyAdjustsScrollIndicatorInsets: true, bounces: true, bouncesZoom: true, canCancelContentTouches: true, diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index bbd855bdecb63f..f0277824873850 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -254,6 +254,12 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & scrollView.snapToOffsets = snapToOffsets; } + if (@available(iOS 13.0, *)) { + if (oldScrollViewProps.automaticallyAdjustsScrollIndicatorInsets != newScrollViewProps.automaticallyAdjustsScrollIndicatorInsets) { + scrollView.automaticallyAdjustsScrollIndicatorInsets = newScrollViewProps.automaticallyAdjustsScrollIndicatorInsets; + } + } + if (@available(iOS 11.0, *)) { if (oldScrollViewProps.contentInsetAdjustmentBehavior != newScrollViewProps.contentInsetAdjustmentBehavior) { auto const contentInsetAdjustmentBehavior = newScrollViewProps.contentInsetAdjustmentBehavior; diff --git a/React/Views/ScrollView/RCTScrollView.m b/React/Views/ScrollView/RCTScrollView.m index 77f1c62b5a1b66..5fa44b56bd8d92 100644 --- a/React/Views/ScrollView/RCTScrollView.m +++ b/React/Views/ScrollView/RCTScrollView.m @@ -928,6 +928,18 @@ -(type)getter \ RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat); RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, scrollIndicatorInsets, UIEdgeInsets); +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */ +- (void)setAutomaticallyAdjustsScrollIndicatorInsets:(BOOL)automaticallyAdjusts API_AVAILABLE(ios(13.0)) +{ + // `automaticallyAdjustsScrollIndicatorInsets` is available since iOS 13. + if ([_scrollView respondsToSelector:@selector(setAutomaticallyAdjustsScrollIndicatorInsets:)]) { + if (@available(iOS 13.0, *)) { + _scrollView.automaticallyAdjustsScrollIndicatorInsets = automaticallyAdjusts; + } + } +} +#endif + #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ - (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBehavior)behavior API_AVAILABLE(ios(11.0)) { diff --git a/React/Views/ScrollView/RCTScrollViewManager.m b/React/Views/ScrollView/RCTScrollViewManager.m index 086a05450cbd6d..41973b0515ceac 100644 --- a/React/Views/ScrollView/RCTScrollViewManager.m +++ b/React/Views/ScrollView/RCTScrollViewManager.m @@ -102,6 +102,9 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollBegin, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL) +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */ +RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL) +#endif #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior) #endif diff --git a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp index 0d34dc12e6cd56..8addef6feff711 100644 --- a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp +++ b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp @@ -51,6 +51,11 @@ ScrollViewProps::ScrollViewProps( "automaticallyAdjustContentInsets", sourceProps.automaticallyAdjustContentInsets, {})), + automaticallyAdjustsScrollIndicatorInsets(convertRawProp( + rawProps, + "automaticallyAdjustsScrollIndicatorInsets", + sourceProps.automaticallyAdjustsScrollIndicatorInsets, + true)), decelerationRate(convertRawProp( rawProps, "decelerationRate", @@ -206,6 +211,10 @@ SharedDebugStringConvertibleList ScrollViewProps::getDebugProps() const { "automaticallyAdjustContentInsets", automaticallyAdjustContentInsets, defaultScrollViewProps.automaticallyAdjustContentInsets), + debugStringConvertibleItem( + "automaticallyAdjustsScrollIndicatorInsets", + automaticallyAdjustsScrollIndicatorInsets, + defaultScrollViewProps.automaticallyAdjustsScrollIndicatorInsets), debugStringConvertibleItem( "decelerationRate", decelerationRate, diff --git a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h index 74ea1045f0d952..31c3a2e919bbd6 100644 --- a/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h +++ b/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h @@ -28,6 +28,7 @@ class ScrollViewProps final : public ViewProps { bool canCancelContentTouches{true}; bool centerContent{}; bool automaticallyAdjustContentInsets{}; + bool automaticallyAdjustsScrollIndicatorInsets{true}; Float decelerationRate{0.998f}; bool directionalLockEnabled{}; ScrollViewIndicatorStyle indicatorStyle{}; diff --git a/packages/rn-tester/js/examples/ScrollView/ScrollViewIndicatorInsetsExample.ios.js b/packages/rn-tester/js/examples/ScrollView/ScrollViewIndicatorInsetsExample.ios.js new file mode 100644 index 00000000000000..6dbbf02e2bb08f --- /dev/null +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewIndicatorInsetsExample.ios.js @@ -0,0 +1,116 @@ +'use strict'; + +const React = require('react'); + +const { + Button, + Dimensions, + Modal, + ScrollView, + StyleSheet, + Switch, + Text, + View, +} = require('react-native'); + +class ScrollViewIndicatorInsetsExample extends React.Component< + {...}, + {| + enableAutoIndicatorInsets: boolean, + modalPresentationStyle: string, + modalVisible: boolean, + |}, +> { + state = { + enableAutoIndicatorInsets: true, + modalPresentationStyle: null, + modalVisible: false, + }; + + _setModalVisible = (modalVisible, modalPresentationStyle) => { + this.setState({ + enableAutoIndicatorInsets: true, + modalVisible, + modalPresentationStyle, + }); + }; + + _setEnableAutoIndicatorInsets = enableAutoIndicatorInsets => { + this.setState({ + enableAutoIndicatorInsets, + }); + }; + + render() { + const {height, width} = Dimensions.get('window'); + return ( + + this._setModalVisible(false)} + presentationStyle={this.state.modalPresentationStyle} + statusBarTranslucent={false} + supportedOrientations={['portrait', 'landscape']}> + + + automaticallyAdjustsScrollIndicatorInsets + this._setEnableAutoIndicatorInsets(v)} + value={this.state.enableAutoIndicatorInsets} + style={styles.switch}/> +