Skip to content

Commit a5a577a

Browse files
fix(ios): default contentInsetAdjustmentBehavior to scrollableAxes on iOS 26+
On iOS 26+, Apple introduced the liquid glass design language with translucent system chrome (tab bars, toolbars). Scroll views need contentInsetAdjustmentBehavior set to scrollableAxes to automatically adjust content insets for this translucent chrome. This change upgrades the default from "never" to "scrollableAxes" on iOS 26+ when UIDesignRequiresCompatibility is not YES, affecting: - RCTEnhancedScrollView initWithFrame: - RCTScrollViewComponentView updateProps: (upgrades JS default "never") - RCTScrollViewComponentView prepareForRecycle Apps that explicitly set contentInsetAdjustmentBehavior to "automatic", "always", or "scrollableAxes" from JS are unaffected. Apps with UIDesignRequiresCompatibility=YES retain the current "never" default. Fixes #57299 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 066c0d8 commit a5a577a

2 files changed

Lines changed: 27 additions & 9 deletions

File tree

packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@ + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
3131
- (instancetype)initWithFrame:(CGRect)frame
3232
{
3333
if (self = [super initWithFrame:frame]) {
34-
// We set the default behavior to "never" so that iOS
35-
// doesn't do weird things to UIScrollView insets automatically
36-
// and keeps it as an opt-in behavior.
37-
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
34+
if (@available(iOS 26, *)) {
35+
NSNumber *compat = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"];
36+
if (!compat.boolValue) {
37+
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentScrollableAxes;
38+
} else {
39+
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
40+
}
41+
} else {
42+
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
43+
}
3844

3945
// We intentionally force `UIScrollView`s `semanticContentAttribute` to `LTR` here
4046
// because this attribute affects a position of vertical scrollbar; we don't want this

packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,13 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
436436

437437
if ((oldScrollViewProps.contentInsetAdjustmentBehavior != newScrollViewProps.contentInsetAdjustmentBehavior) ||
438438
_shouldUpdateContentInsetAdjustmentBehavior) {
439-
const auto contentInsetAdjustmentBehavior = newScrollViewProps.contentInsetAdjustmentBehavior;
439+
auto contentInsetAdjustmentBehavior = newScrollViewProps.contentInsetAdjustmentBehavior;
440+
if (@available(iOS 26, *)) {
441+
NSNumber *compat = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"];
442+
if (!compat.boolValue && contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Never) {
443+
contentInsetAdjustmentBehavior = ContentInsetAdjustmentBehavior::ScrollableAxes;
444+
}
445+
}
440446
if (contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Never) {
441447
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
442448
} else if (contentInsetAdjustmentBehavior == ContentInsetAdjustmentBehavior::Automatic) {
@@ -698,10 +704,16 @@ - (void)prepareForRecycle
698704
_contentSize = CGSizeZero;
699705
// Reset contentInset to prevent stale insets leaking into recycled scroll views.
700706
_scrollView.contentInset = UIEdgeInsetsZero;
701-
// We set the default behavior to "never" so that iOS
702-
// doesn't do weird things to UIScrollView insets automatically
703-
// and keeps it as an opt-in behavior.
704-
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
707+
if (@available(iOS 26, *)) {
708+
NSNumber *compat = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"];
709+
if (!compat.boolValue) {
710+
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentScrollableAxes;
711+
} else {
712+
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
713+
}
714+
} else {
715+
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
716+
}
705717
_shouldUpdateContentInsetAdjustmentBehavior = YES;
706718
_isUserTriggeredScrolling = NO;
707719
CGRect oldFrame = self.frame;

0 commit comments

Comments
 (0)