Skip to content
48 changes: 47 additions & 1 deletion ios/tabs/host/RNSTabBarController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

#define RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE !TARGET_OS_TV && !TARGET_OS_VISION

// https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc#The-More-navigation-controller
static constexpr NSUInteger kMinCountOfVCsForMoreVCPresence = 6;

// We need UINavigationControllerDelegate to handle navigation within `moreNavigationController`
@interface RNSTabBarController () <UITabBarControllerDelegate, UINavigationControllerDelegate>
@end
Expand Down Expand Up @@ -185,6 +188,20 @@ - (void)setSelectedViewController:(__kindof UIViewController *)selectedViewContr
}
}

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
[super traitCollectionDidChange:previousTraitCollection];

if (previousTraitCollection == nil || self.selectedViewController == nil) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you may consider combining all 4 conditions, I don't see any reason for splitting this, until you're planning to add some logic/logs before that early return disableNavigationBarInMoreNavigationController

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that, I think its just thing of aesthetics - I did that deliberately to separate the guard statements from actual logic. I'll leave it as is.

return;
}

if (self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass &&
[self isViewControllerHostedByMoreNavigationController:self.selectedViewController]) {
[self disableNavigationBarInMoreNavigationController];
}
}

#pragma mark - Signals

- (void)setPendingNavigationStateUpdate:(nullable RNSTabsNavigationStateUpdateRequest *)stateUpdate
Expand Down Expand Up @@ -498,6 +515,8 @@ - (void)updateSelectedViewController
*/
- (void)updateSelectedViewControllerInner
{
RCTAssert(_pendingStateUpdate != nil, @"[RNScreens] Pending update MUST NOT be nil");

UIViewController *_Nonnull currSelectedViewController = self.selectedViewController;

NSString *_Nonnull nextSelectedViewControllerKey = _pendingStateUpdate.selectedScreenKey;
Expand Down Expand Up @@ -546,6 +565,10 @@ - (void)updateSelectedViewControllerInner
BOOL hasStateProgressed = [self updateSelectedViewControllerTo:nextSelectedViewController
withKey:nextSelectedViewControllerKey];

if (hasStateProgressed && [self isViewControllerHostedByMoreNavigationController:nextSelectedViewController]) {
[self disableNavigationBarInMoreNavigationController];
}

if (hasStateProgressed) {
RNSTabsNavigationStateUpdateContext *context =
[[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
Expand Down Expand Up @@ -696,6 +719,10 @@ - (void)reconcileNavigationStateWithUIKitState
selectedScreenKey);
[self progressNavigationState:selectedScreenKey withOrigin:RNSTabsActionOriginImplicit];

if ([self isViewControllerHostedByMoreNavigationController:self.selectedViewController]) {
[self disableNavigationBarInMoreNavigationController];
}

auto *context = [[RNSTabsNavigationStateUpdateContext alloc] initWithNavState:_navigationState
isRepeated:NO
hasTriggeredSpecialEffect:NO
Expand Down Expand Up @@ -725,12 +752,31 @@ - (BOOL)canHaveMoreNavigationController
{
#if RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
// https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc#The-More-navigation-controller
return self.viewControllers.count > 5;
// The count is documented. Size class check is empirical, to tighten the condition and have less
// false positives. If we ever find it not correct, we can safely remove it.
return self.viewControllers.count >= kMinCountOfVCsForMoreVCPresence &&
self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact;
Comment thread
kkafar marked this conversation as resolved.
#else
return NO;
#endif // RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
}

- (BOOL)isViewControllerHostedByMoreNavigationController:(nonnull UIViewController *)viewController
{
if (![self canHaveMoreNavigationController] || ![self isMoreNavigationControllerPresentInTabBar]) {
return NO;
}

// Guard: VC must be one we manage (excludes arbitrary external VCs).
if ([self.viewControllers indexOfObject:viewController] == NSNotFound) {
return NO;
}

// Ground truth: if our VC's tabBarItem is NOT in the visible tab bar, it is hosted by the
// More navigation controller. Correct even when users reorder tabs via the More list's Edit UI.
return ![self.tabBar.items containsObject:viewController.tabBarItem];
}

- (BOOL)isViewControllerTheMoreNavigationController:(nonnull UIViewController *)viewController
{
#if RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
Expand Down
Loading