Skip to content
Merged
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
117 changes: 73 additions & 44 deletions ios/RNSScreenStackHeaderSubview.mm
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,17 @@ - (void)updateLayoutMetrics:(const react::LayoutMetrics &)layoutMetrics
self);
} else {
#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
BOOL sizeHasChanged = _layoutMetrics.frame.size != layoutMetrics.frame.size;
_layoutMetrics = layoutMetrics;
if (sizeHasChanged) {
[self invalidateIntrinsicContentSize];
}
#else // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
self.bounds = CGRect{CGPointZero, frame.size};
if (self.needsAutoLayout) {
BOOL sizeHasChanged = _layoutMetrics.frame.size != layoutMetrics.frame.size;
_layoutMetrics = layoutMetrics;
if (sizeHasChanged) {
[self invalidateIntrinsicContentSize];
}
} else
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
{
self.bounds = CGRect{CGPointZero, frame.size};
}
[self layoutNavigationBar];
}
}
Expand All @@ -206,56 +209,82 @@ - (void)reactSetFrame:(CGRect)frame
// makes UINavigationBar the only one to control the position of header content.
if (!CGSizeEqualToSize(frame.size, self.frame.size)) {
#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
_lastReactFrameSize = frame.size;
[self invalidateIntrinsicContentSize];
#else // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
[super reactSetFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
if (self.needsAutoLayout) {
_lastReactFrameSize = frame.size;
[self invalidateIntrinsicContentSize];
} else
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
{
[super reactSetFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
}
[self layoutNavigationBar];
}
}

#endif // RCT_NEW_ARCH_ENABLED

#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)

// Starting from iOS 26, to center left and right subviews inside liquid glass backdrop,
// we need to use auto layout. To make Yoga's layout work with auto layout, we pass information
// from Yoga via `intrinsicContentSize`.
- (BOOL)needsAutoLayout
{
BOOL needsAutoLayout = NO;
if (@available(iOS 26.0, *)) {
needsAutoLayout = _type == RNSScreenStackHeaderSubviewTypeLeft || _type == RNSScreenStackHeaderSubviewTypeRight;
}
return needsAutoLayout;
}

#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)

#pragma mark - UIBarButtonItem specific

- (UIBarButtonItem *)getUIBarButtonItem
{
RCTAssert(
_type == RNSScreenStackHeaderSubviewTypeLeft || _type == RNSScreenStackHeaderSubviewTypeRight,
@"[RNScreens] Unexpected subview type.");

if (_barButtonItem == nil) {
#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
// Starting from iOS 26, UIBarButtonItem's customView is streched to have at least 36 width.
// Stretching RNSScreenStackHeaderSubview means that its subviews are aligned to left instead
// of the center. To mitigate this, we add a wrapper view that will center
// RNSScreenStackHeaderSubview inside of itself.
UIView *wrapperView = [UIView new];
wrapperView.translatesAutoresizingMaskIntoConstraints = NO;

self.translatesAutoresizingMaskIntoConstraints = NO;
[wrapperView addSubview:self];

[self.centerXAnchor constraintEqualToAnchor:wrapperView.centerXAnchor].active = YES;
[self.centerYAnchor constraintEqualToAnchor:wrapperView.centerYAnchor].active = YES;

// To prevent UIKit from stretching subviews to all available width, we need to:
// 1. Set width of wrapperView to match RNSScreenStackHeaderSubview BUT when
// RNSScreenStackHeaderSubview's width is smaller that minimal required 36 width, it breaks
// UIKit's constraint. That's why we need to lower the priority of the constraint.
NSLayoutConstraint *widthEqual = [wrapperView.widthAnchor constraintEqualToAnchor:self.widthAnchor];
widthEqual.priority = UILayoutPriorityDefaultHigh;
widthEqual.active = YES;

NSLayoutConstraint *heightEqual = [wrapperView.heightAnchor constraintEqualToAnchor:self.heightAnchor];
heightEqual.priority = UILayoutPriorityDefaultHigh;
heightEqual.active = YES;

// 2. Set content hugging priority for RNSScreenStackHeaderSubview.
[self setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

_barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:wrapperView];
#else // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
_barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self];
if (@available(iOS 26.0, *)) {
// Starting from iOS 26, UIBarButtonItem's customView is streched to have at least 36 width.
// Stretching RNSScreenStackHeaderSubview means that its subviews are aligned to left instead
// of the center. To mitigate this, we add a wrapper view that will center
// RNSScreenStackHeaderSubview inside of itself.
UIView *wrapperView = [UIView new];
wrapperView.translatesAutoresizingMaskIntoConstraints = NO;

self.translatesAutoresizingMaskIntoConstraints = NO;
[wrapperView addSubview:self];

[self.centerXAnchor constraintEqualToAnchor:wrapperView.centerXAnchor].active = YES;
[self.centerYAnchor constraintEqualToAnchor:wrapperView.centerYAnchor].active = YES;

// To prevent UIKit from stretching subviews to all available width, we need to:
// 1. Set width of wrapperView to match RNSScreenStackHeaderSubview BUT when
// RNSScreenStackHeaderSubview's width is smaller that minimal required 36 width, it breaks
// UIKit's constraint. That's why we need to lower the priority of the constraint.
NSLayoutConstraint *widthEqual = [wrapperView.widthAnchor constraintEqualToAnchor:self.widthAnchor];
widthEqual.priority = UILayoutPriorityDefaultHigh;
widthEqual.active = YES;

NSLayoutConstraint *heightEqual = [wrapperView.heightAnchor constraintEqualToAnchor:self.heightAnchor];
heightEqual.priority = UILayoutPriorityDefaultHigh;
heightEqual.active = YES;

// 2. Set content hugging priority for RNSScreenStackHeaderSubview.
[self setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

_barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:wrapperView];
} else
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
{
_barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self];
}
[self configureBarButtonItem];
}

Expand All @@ -281,7 +310,7 @@ - (void)configureBarButtonItem
[_barButtonItem setHidesSharedBackground:_hidesSharedBackground];
}
}
#endif
#endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
}

- (void)setHidesSharedBackground:(BOOL)hidesSharedBackground
Expand Down
Loading