Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -385,16 +385,36 @@ export type ScrollViewPropsIOS = Readonly<{
* @platform ios
*/
zoomScale?: ?number,

/**
* How the safe area insets modify the content area of the scroll view.
* Controls whether and how the ScrollView keeps its content clear of the
* system safe areas (iOS only).
*
* `'automatic'` is strongly recommended anywhere a ScrollView or FlatList
* is the root element of a given screen.
*
* Along the scrollable axis, `'always'` applies the safe area insets.
* `'automatic'` and `'scrollableAxes'` apply them while the content is
* scrollable, or while `alwaysBounce*` is true.
*
* Across the scrollable axis, `'automatic'` and `'always'` also pad the
* content container by the insets, such as side insets for an iPhone in
* landscape, or the vertical bar on iPhone Duo.
*
* - **'never'** (default): Applies no insets. Content is laid out and
* scrolls under the system UI.
* - **'automatic'**: Applies insets along the scrollable axis, plus padding
* across it.
* - **'scrollableAxes'**: Uses UIKit's `.scrollableAxes` insets along the
* scrollable axis only.
* - **'always'**: Applies insets on both axes. Unlike `'automatic'`,
* scrollable-axis insets remain when content fits and `alwaysBounce*` is
* false.
*
* Along the scrollable axis, the insets become content insets, so content
* scrolls under the system UI. Across it, every value except `'never'` pads
* the content container by the insets, so content is laid out clear of the
* system UI, such as the rail on iPhone Duo or camera cutout in landscape,
* while the container's background still extends edge to edge. On every
* edge, the `contentContainerStyle` padding counts toward the inset, so
* content sits at the larger of the two.
* Note: With any value other than `'never'`, point-valued
* `contentContainerStyle` padding on edges where an inset applies is
* absorbed by the inset, instead of adding to it. See
* `contentContainerStyle`.
*
* @default `'never'`
* @platform ios
Expand Down Expand Up @@ -509,7 +529,7 @@ type ScrollViewBaseProps = Readonly<{
* If setting `contentInsetAdjustmentBehavior` (recommended), `padding` is
* applied slightly differently than with a regular `<View>`:
*
* - Pixel values: If a system safe area inset applies on a given edge, the
* - Point values: If a system safe area inset applies on a given edge, the
* **greater of** the `padding` or inset length will be applied. An edge
* without an inset, such as the top of a ScrollView below a header, keeps
* its full `padding`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ NS_ASSUME_NONNULL_BEGIN

/*
* Whether `ScrollView` pads the content container by the safe area across the scrollable axis, which it does when
* its `contentInsetAdjustmentBehavior` prop is not `never`. UIKit's own `contentInsetAdjustmentBehavior` can differ,
* as react-native-screens sets it natively on the first scroll view of a screen.
* its `contentInsetAdjustmentBehavior` prop is `automatic` or `always`. UIKit's own `contentInsetAdjustmentBehavior`
* can differ, as react-native-screens sets it natively on the first scroll view of a screen.
*/
@property (nonatomic, assign) BOOL padsContentForSafeArea;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ NS_ASSUME_NONNULL_BEGIN

/*
* The safe area the content is laid out clear of, as a floor on the content container's own padding. The enclosing
* scroll view sets the pair of insets across its scrollable axis, and zero while its `contentInsetAdjustmentBehavior`
* is `never`.
* scroll view sets the pair of insets across its scrollable axis while its `contentInsetAdjustmentBehavior` is
* `automatic` or `always`, and zero otherwise.
*/
@property (nonatomic, readonly) UIEdgeInsets safeAreaPadding;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ static UIScrollViewIndicatorStyle RCTUIScrollViewIndicatorStyleFromProps(const S
userInfo:userInfo];
}

/*
* Whether React Native pads the content container by the safe area across the scrollable axis. Along-axis insets
* still come from UIKit for every value except `never`.
*/
static BOOL RCTPadsContentForSafeArea(ContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior)
{
return contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Automatic ||
contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Always;
}

@interface RCTScrollViewComponentView () <
UIScrollViewDelegate,
RCTScrollViewProtocol,
Expand Down Expand Up @@ -451,7 +461,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
}
((RCTEnhancedScrollView *)_scrollView).padsContentForSafeArea =
contentInsetAdjustmentBehavior != ContentInsetAdjustmentBehavior::Never;
RCTPadsContentForSafeArea(contentInsetAdjustmentBehavior);
_shouldUpdateContentInsetAdjustmentBehavior = NO;
}

Expand Down Expand Up @@ -894,33 +904,39 @@ - (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView
}

/*
* Hands the content container the safe area to lay its content out clear of, unless `contentInsetAdjustmentBehavior`
* is `never`: the pair of insets across the scrollable axis, which it pads by, and along the axis the content inset
* UIKit adds for the safe area, which its own padding counts toward. Content still scrolls under the system UI along
* the axis.
* Hands the content container the safe area to lay its content out clear of: along the scrollable axis, the content
* inset UIKit adds for the safe area, which point-valued padding counts toward, unless
* `contentInsetAdjustmentBehavior` is `never`; and across the axis, the pair of insets it pads by, for `automatic`
* and `always`. Content still scrolls under the system UI along the axis.
*/
- (void)_updateContentViewSafeAreaPadding
{
if (![_contentView isKindOfClass:[RCTScrollContentViewComponentView class]]) {
return;
}
const auto &props = static_cast<const ScrollViewProps &>(*_props);
auto *scrollView = (RCTEnhancedScrollView *)_scrollView;
UIEdgeInsets padding = UIEdgeInsetsZero;
UIEdgeInsets contentInset = UIEdgeInsetsZero;
if (props.contentInsetAdjustmentBehavior != ContentInsetAdjustmentBehavior::Never) {
Comment thread
huntie marked this conversation as resolved.
Resolved
UIEdgeInsets adjusted = scrollView.adjustedContentInset;
UIEdgeInsets current = scrollView.contentInset;
if (props.horizontal) {
contentInset.left = adjusted.left - current.left;
contentInset.right = adjusted.right - current.right;
} else {
contentInset.top = adjusted.top - current.top;
contentInset.bottom = adjusted.bottom - current.bottom;
}
}
if (scrollView.padsContentForSafeArea) {
UIEdgeInsets insets = self.safeAreaInsets;
UIEdgeInsets adjusted = _scrollView.adjustedContentInset;
UIEdgeInsets current = _scrollView.contentInset;
if (props.horizontal) {
padding.top = insets.top;
padding.bottom = insets.bottom;
contentInset.left = adjusted.left - current.left;
contentInset.right = adjusted.right - current.right;
} else {
padding.left = insets.left;
padding.right = insets.right;
contentInset.top = adjusted.top - current.top;
contentInset.bottom = adjusted.bottom - current.bottom;
}
}
[(RCTScrollContentViewComponentView *)_contentView setSafeAreaPadding:padding contentInsetAdjustment:contentInset];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ScrollContentViewState final {
/*
* The safe area the content is laid out clear of, as a floor on the padding
* the style sets. The enclosing scroll view sets the pair of edges across
* its scrollable axis.
* its scrollable axis for `automatic` and `always`.
*/
EdgeInsets safeAreaPadding{};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ const examples: Array<RNTesterModuleExample> = [
{
title: '<ScrollView> Content Inset Adjustment Behavior\n',
description:
'How the safe area modifies the content area of a full screen ScrollView: along the scrollable axis it becomes content insets, across it the content container is padded, and on every edge the container padding counts toward it.',
'How the safe area modifies the content area of a full screen ScrollView: scrollableAxes uses UIKit .scrollableAxes for along-axis insets, automatic and always also pad across the axis, and point-valued container padding counts toward active inset edges.',
platform: 'ios',
render(): React.Node {
return <ContentInsetAdjustmentBehaviorExample />;
Expand Down
Loading