Skip to content
Closed
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
8 changes: 7 additions & 1 deletion ios/RNSScreenStackHeaderConfig.mm
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,13 @@ - (void)updateHeaderStateInShadowTreeInContextOfNavigationBar:(nullable UINaviga
- (NSDirectionalEdgeInsets)computeEdgeInsetsOfNavigationBar:(nonnull UINavigationBar *)navigationBar
{
NSDirectionalEdgeInsets navBarMargins = [navigationBar directionalLayoutMargins];
NSDirectionalEdgeInsets navBarContentMargins = [navigationBar.rnscreens_findContentView directionalLayoutMargins];
NSDirectionalEdgeInsets navBarContentMargins;
if (@available(iOS 26.0, *)) {
UIView *titleControl = [UINavigationBar findTitleControlInView:navigationBar];
navBarContentMargins = [navigationBar rnscreens_computeTotalEdgeInsetsForView:titleControl];
} else {
navBarContentMargins = [navigationBar.rnscreens_findContentView directionalLayoutMargins];
}

BOOL isDisplayingBackButton = [self shouldBackButtonBeVisibleInNavigationBar:navigationBar];

Expand Down
40 changes: 38 additions & 2 deletions ios/utils/UINavigationBar+RNSUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* Tested to work reliably on iOS 18.0, 17.5, 15.5.
*
* @returns `_UINavigationBarContentView` view mounted directly under the navigation bar itself
* @return `_UINavigationBarContentView` view mounted directly under the navigation bar itself
*/
- (nullable UIView *)rnscreens_findContentView;

Expand All @@ -27,11 +27,47 @@ NS_ASSUME_NONNULL_BEGIN
*
* Tested to work reliably on iOS 18.0, 17.5, 15.5.
*
* @returns `_UIButtonBarButton` view, if present and mounted in anticipated place;
* @return `_UIButtonBarButton` view, if present and mounted in anticipated place;
* if the back button is not present, this method returns `nil`.
*/
- (nullable UIView *)rnscreens_findBackButtonWrapperView;

/**
* @brief Responsible for calculating the cumulative directional layout margins the will be applied to the title in the
* header.
*
* This method traverses the superview hierarchy, starting from the parent of the specified source view,
* and accumulates all `directionalLayoutMargins` until it reaches the `UINavigationBar`.
* The resulting insets represent the total margin space between the source view and its navigation container.
*
* Tested to work reliably on iOS 26.0, 18.5.
*
* @param sourceView The UIView from which to begin traversing up towards the navigation bar.
* @return `NSDirectionalEdgeInsets` containing the combined layout margins from the title control
* up to its ancestor navigation bar. Excluding the layout margins of both the:
* - source view - because we're positioning relatively to this view
* - `UINavigationBar` - because we're calculating it inside computeEdgeInsetsOfNavigationBar explicitly
*/
- (NSDirectionalEdgeInsets)rnscreens_computeTotalEdgeInsetsForView:(nullable UIView *)sourceView;

/**
* @brief Responsible for searching the view hierarchy to locate the `_UINavigationBarTitleControl` view.
*
* This method traverses a subtree from given view, to find `_UINavigationBarTitleControl`.
* `_UINavigationBarTitleControl` is a private API. Therefore, this method relies on the
* hardcoded class name which may change at any time.
*
* The method performs a DFS starting from the provided view.
* Once a matching title control is found, it is returned immediately. If not found,
* the method returns nil.
*
* Tested to work reliably on iOS 26.0, 18.5.
*
* @param view The root UIView from which to begin searching the view hierarchy.
* @return `UIView` instance representing the `_UINavigationBarTitleControl` or `nil`.
*/
+ (UIView *)findTitleControlInView:(UIView *)view;

@end

NS_ASSUME_NONNULL_END
41 changes: 41 additions & 0 deletions ios/utils/UINavigationBar+RNSUtility.mm
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#import "UINavigationBar+RNSUtility.h"

#import <React/RCTLog.h>

@implementation UINavigationBar (RNSUtility)

- (nullable UIView *)rnscreens_findContentView
Expand Down Expand Up @@ -83,4 +85,43 @@ + (Class)rnscreens_getContentViewRuntimeClass
return NSClassFromString(@"_UINavigationBarContentView"); // Sampled from iOS 17.5 (iPhone 15 Pro)
}

- (NSDirectionalEdgeInsets)rnscreens_computeTotalEdgeInsetsForView:(nullable UIView *)sourceView
{
NSDirectionalEdgeInsets totalMargins = NSDirectionalEdgeInsetsMake(0, 0, 0, 0);

if (sourceView == nil) {
RCTLogWarn(@"[RNScreens] _UINavigationBarTitleControl not found under the UINavigationBar hierarchy");
return totalMargins;
}

UIView *currentView = sourceView.superview;
while (currentView != nil && currentView != self) {
NSDirectionalEdgeInsets margins = currentView.directionalLayoutMargins;
totalMargins.top += margins.top;
totalMargins.leading += margins.leading;
totalMargins.bottom += margins.bottom;
totalMargins.trailing += margins.trailing;
currentView = currentView.superview;
}
return totalMargins;
}

+ (UIView *)findTitleControlInView:(UIView *)view
{
static Class UINavBarTitleControlClass = NSClassFromString(@"_UINavigationBarTitleControl");

if ([view isKindOfClass:UINavBarTitleControlClass]) {
return view;
}

for (UIView *subview in view.subviews) {
UIView *result = [UINavigationBar findTitleControlInView:subview];
if (result) {
return result;
}
}

return nil;
}

@end
Loading