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 9c25f225e5f54d..14c4803a3247e8 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -251,6 +251,14 @@ - (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..0ef49b259bccaf --- /dev/null +++ b/packages/rn-tester/js/examples/ScrollView/ScrollViewIndicatorInsetsExample.ios.js @@ -0,0 +1,112 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +import * as React from 'react'; + +import { + Button, + Modal, + ScrollView, + StyleSheet, + Switch, + Text, + View, + useWindowDimensions, +} from 'react-native'; + +export function ScrollViewIndicatorInsetsExample() { + const [automaticallyAdjustsScrollIndicatorInsets, setAutomaticallyAdjustsScrollIndicatorInsets] = React.useState(true); + const [modalVisible, setModalVisible] = React.useState(false); + const { height, width } = useWindowDimensions(); + + return ( + + setModalVisible(false)} + presentationStyle="fullScreen" + statusBarTranslucent={false} + supportedOrientations={['portrait', 'landscape']}> + + + + When automaticallyAdjustsScrollIndicatorInsets is true, the scrollbar is inset to the status bar. When false, it reaches the edge of the modal. + Check out the UIScrollView docs to learn more about automaticallyAdjustsScrollIndicatorInsets + + + automaticallyAdjustsScrollIndicatorInsets is {automaticallyAdjustsScrollIndicatorInsets + ''} + setAutomaticallyAdjustsScrollIndicatorInsets(v)} + value={automaticallyAdjustsScrollIndicatorInsets} + style={styles.switch}/> + +