@@ -278,6 +278,7 @@ - (void)userDidSelectViewController:(nonnull UIViewController *)viewController
278278 [self updateNavigationStateOnModelUpdate ];
279279
280280 if ([self isSelectedViewControllerTheMoreNavigationController ]) {
281+ [self disableNavigationBarInMoreNavigationController ];
281282 [self setupMoreNavigationControllerDelegateIfNeeded ];
282283 }
283284
@@ -348,9 +349,23 @@ - (BOOL)tabBarController:(UITabBarController *)tabBarController
348349 if (shouldPreventTabSelection) {
349350 // Ideally we'd call this AFTER we prevent, but there is no appropriate callback.
350351 [self onDidPreventUserFromSelectingViewControllerWithKey: [self screenKeyForViewController: viewController]];
352+ return NO ;
351353 }
352354
353- return !shouldPreventTabSelection;
355+ // If we're gonna allow navigation to `moreNavigationController`, then we need to ensure
356+ // that on top of its stack there is no controller with preventNativeSelection enabled.
357+ // In such case, we want to pop to root.
358+ // We do it here, because in `tabBarController:didSelectViewController:` we won't receive
359+ // `moreNavigationController` in case there is already a tab pushed on the stack.
360+ if ([self isViewControllerTheMoreNavigationController: viewController]) {
361+ auto *poppedViewController = [self popToRootInMoreNavigationControllerRespectSelectionPrevention: YES animated: NO ];
362+ if (poppedViewController != nil ) {
363+ // We actually popped something -> let's notify JS realm of this fact.
364+ [self onDidPreventUserFromSelectingViewControllerWithKey: [self screenKeyForViewController: poppedViewController]];
365+ }
366+ }
367+
368+ return YES ;
354369}
355370
356371- (void )tabBarController : (UITabBarController *)tabBarController
@@ -366,10 +381,6 @@ - (void)tabBarController:(UITabBarController *)tabBarController
366381 @" [RNScreens] Unexpected type of controller: %@ " ,
367382 viewController.class );
368383
369- if (isNextViewControllerMoreNavigationController) {
370- [self disableNavigationBarInMoreNavigationController ];
371- }
372-
373384 [self userDidSelectViewController: viewController];
374385}
375386
@@ -513,7 +524,7 @@ - (void)updateSelectedViewControllerInner
513524
514525 // Animate only if we're currently in context of more view controller.
515526 BOOL shouldAnimate = [self isMoreNavigationControllerTabBarItemSelected ];
516- [self popToRootInMoreNavigationControllerIfNeededAnimated : shouldAnimate];
527+ [self popToRootInMoreNavigationControllerRespectSelectionPrevention: NO animated : shouldAnimate];
517528
518529 // Also disable the header - we don't control it, but it impacts the layout
519530 // in ways Yoga is not aware of. The simplest option here is to disable it.
@@ -722,18 +733,56 @@ - (BOOL)isMoreNavigationControllerTabBarItemSelected
722733- (void )disableNavigationBarInMoreNavigationController
723734{
724735#if RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
725- [self .moreNavigationController setNavigationBarHidden: YES animated: NO ];
736+ if (!self.moreNavigationController .navigationBar .isHidden ) {
737+ [self .moreNavigationController setNavigationBarHidden: YES animated: NO ];
738+ }
726739#endif // RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
727740}
728741
729- - (void )popToRootInMoreNavigationControllerIfNeededAnimated : (BOOL )isAnimated
742+ - (nullable UIViewController *)popToRootInMoreNavigationControllerRespectSelectionPrevention :
743+ (BOOL )shouldRespectSelectionPrevention
744+ animated : (BOOL )shouldAnimate
730745{
731746#if RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
732747 if ([self isMoreNavigationControllerPresentInTabBar ] && self.moreNavigationController .viewControllers .count > 1 ) {
733748 // We quietly assume here, that the root view controller is the `UIMoreListViewController`.
734- [self .moreNavigationController popToRootViewControllerAnimated: isAnimated];
749+ if (shouldRespectSelectionPrevention) {
750+ UIViewController *topViewController = self.moreNavigationController .topViewController ;
751+ RCTAssert (
752+ [topViewController isKindOfClass: RNSTabsScreenViewController.class ],
753+ @" [RNScreens] Unexpected type of view controller on moreNavigationControllerStack: %@ " ,
754+ topViewController.class );
755+ RNSTabsScreenViewController *screenController = static_cast <RNSTabsScreenViewController *>(topViewController);
756+ if (screenController.isPreventNativeSelectionEnabled ) {
757+ return [self popToRootMoreNavigationController: self .moreNavigationController animated: shouldAnimate];
758+ }
759+ } else {
760+ return [self popToRootMoreNavigationController: self .moreNavigationController animated: shouldAnimate];
761+ }
735762 }
736763#endif // RNS_MORE_NAVIGATION_CONTROLLER_AVAILABLE
764+ return nil ;
765+ }
766+
767+ /* *
768+ * Pops the top view controller from more navigation controller. We expect at most two controllers on the stack of more
769+ * navigation controller. If this assumption ever becomes invalid, this method needs to be updated.
770+ *
771+ * @returns nil if there was nothing to pop, the topViewController otherwise.
772+ */
773+ - (nullable UIViewController *)popToRootMoreNavigationController :
774+ (nonnull UINavigationController *)moreNavigationController
775+ animated : (BOOL )animated
776+ {
777+ if (moreNavigationController.viewControllers .count < 2 ) {
778+ return nil ;
779+ }
780+
781+ auto *poppedViewControllers = [moreNavigationController popToRootViewControllerAnimated: animated];
782+ RCTAssert (
783+ poppedViewControllers != nil && poppedViewControllers.count == 1 ,
784+ @" [RNScreens] Expected exactly one view controller to be popped" );
785+ return [poppedViewControllers firstObject ];
737786}
738787
739788- (void )setupMoreNavigationControllerDelegateIfNeeded
0 commit comments