UITabBarController + UINavigationController和更多选项卡黑屏问题

我目前正在对我的一个应用程序进行更新,而且遇到了与UITabBarController有关的一个非常奇怪的问题。

在我的故事板中,我有大约8个视图控制器,在我的UITabBarController子类中,我添加了另外4个以编程方式加载的视图控制器。 大多数这些视图需要UINavigationController保持一致性,当一些视图从“更多”选项卡出现在主要栏中旋转时,为了做到这一点,我已经将它们embedded到UINavigationController中。

如果您在纵向中select视图6,并且在视图在标签栏中获取其自己的button时旋转,则UINavigationController将变黑,但是当它返回到“更多”时,视图会返回。 在我的这些调查中,似乎UINavigationController将UIViewController作为它的根视图控制器来丢失。

按预期工作,不会进入“更多”选项卡:imgur.com/gVB8wTF

黑屏如果视图来自“更多”选项卡: http : //imgur.com/WaoNoL1

我做了一个快速的示例项目,有这个问题: https : //github.com/joshluongo/UITabBarController-Issues

有想法该怎么解决这个吗?

我遇到了同样的问题。

我能够想出一个很好的解决方法。 我把它推到Github这里: https : //github.com/jfahrenkrug/UITabBarControllerMoreBugWorkaround

任何改进都欢迎。

发生这个错误的原因是你的UINavigationController的堆栈被从中移除并放入私有的UIMoreNavigationController。 但是在旋转回常规宽度时,该堆栈不能正确地放回原来的UINavigationViewController。

解决的办法是willTransitionToTraitCollection:withTransitionCoordinator: UITabBarController并用下面的willTransitionToTraitCollection:withTransitionCoordinator:replace它的willTransitionToTraitCollection:withTransitionCoordinator:

 - (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { //#define MORE_TAB_DEBUG 1 #ifdef MORE_TAB_DEBUG #define MoreTabDLog(fmt, ...) NSLog((@"[More Tab Debug] " fmt), ##__VA_ARGS__); #else #define MoreTabDLog(...) #endif MoreTabDLog(@"-- before willTransitionToTraitCollection"); /* There is a bug when going in and out of the compact size class when a tab bar controller has more than 5 tabs. See http://www.openradar.me/25393521 It comes down to this: When you have more than 5 tabs and a view controller on a tab beyond the 4th tab is a UINavigationController, you have a problem. When you are on this tab in compact and push one or more VCs onto the stack and then change back to regular width, only the top most view controller will be added back onto the stack. This happens because the stack of your UINavigationController is taken out of that NavVC and put into the private UIMoreNavigationController. But upon rotating back to regular, that stack is not correctly put back into your own NavVC. We have 3 cases we have to handle: 1) We are on the "More" tab in compact and are looking at the UIMoreListController and then change to regular size. 2) While in compact width, we are on a tab greater than the 4th and are changing to regular width. 3) While in regular width, we are on a tab greater than the 4th and are changing to compact width. */ if ((self.traitCollection.horizontalSizeClass != newCollection.horizontalSizeClass) || (self.traitCollection.verticalSizeClass != newCollection.verticalSizeClass)) { /* Case 1: We are on the "More" tab in compact and are looking at the UIMoreListController and then change to regular size. */ if ([self.selectedViewController isKindOfClass:[UINavigationController class]] && [NSStringFromClass([self.selectedViewController class]) hasPrefix:@"UIMore"]) { // We are on the root of the MoreViewController in compact, going into regular. // That means we have to pop all the viewControllers in the MoreViewController to root #ifdef MORE_TAB_DEBUG UINavigationController *moreNavigationController = (UINavigationController *)self.selectedViewController; UIViewController *moreRootViewController = [moreNavigationController topViewController]; MoreTabDLog(@"-- going OUT of compact while on UIMoreList"); MoreTabDLog(@"moreRootViewController: %@", moreRootViewController); #endif for (NSInteger overflowVCIndex = 4; overflowVCIndex < [self.viewControllers count]; overflowVCIndex++) { if ([self.viewControllers[overflowVCIndex] isKindOfClass:[UINavigationController class]]) { UINavigationController *navigationController = (UINavigationController *)self.viewControllers[overflowVCIndex]; MoreTabDLog(@"popping %@ to root", navigationController); [navigationController popToRootViewControllerAnimated:NO]; } } } else { BOOL isPotentiallyInOverflow = [self.viewControllers indexOfObject:self.selectedViewController] >= 4; MoreTabDLog(@"isPotentiallyInOverflow: %i", isPotentiallyInOverflow); if (isPotentiallyInOverflow && [self.selectedViewController isKindOfClass:[UINavigationController class]]) { UINavigationController *selectedNavController = (UINavigationController *)self.selectedViewController; NSArray<UIViewController *> *selectedNavControllerStack = [selectedNavController viewControllers]; MoreTabDLog(@"Selected Nav: %@, selectedNavStack: %@", selectedNavController, selectedNavControllerStack); UIViewController *lastChildVCOfTabBar = [[self childViewControllers] lastObject]; if ([lastChildVCOfTabBar isKindOfClass:[UINavigationController class]] && [NSStringFromClass([lastChildVCOfTabBar class]) hasPrefix:@"UIMore"]) { /* Case 2: While in compact width, we are on a tab greater than the 4th and are changing to regular width. We are going OUT of compact */ UINavigationController *moreNavigationController = (UINavigationController *)lastChildVCOfTabBar; NSArray *moreNavigationControllerStack = [moreNavigationController viewControllers]; MoreTabDLog(@"--- going OUT of compact"); MoreTabDLog(@"moreNav: %@, moreNavStack: %@, targetNavStack: %@", moreNavigationController, moreNavigationControllerStack, selectedNavControllerStack); if ([moreNavigationControllerStack count] > 1) { NSArray *fixedTargetStack = [moreNavigationControllerStack subarrayWithRange:NSMakeRange(1, moreNavigationControllerStack.count - 1)]; MoreTabDLog(@"fixedTargetStack: %@", fixedTargetStack); dispatch_async(dispatch_get_main_queue(), ^{ NSArray *correctVCList = [NSArray arrayWithArray:self.viewControllers]; [selectedNavController willMoveToParentViewController:self]; [selectedNavController setViewControllers:fixedTargetStack animated:NO]; // We need to do this because without it, the selectedNavController doesn't // have a parentViewController anymore. [self addChildViewController:selectedNavController]; // We need to do this because otherwise the previous call will cause the given // Tab to show up twice in the UIMoreListController. [self setViewControllers:correctVCList]; }); } else { MoreTabDLog(@"popping to root"); dispatch_async(dispatch_get_main_queue(), ^{ [selectedNavController popToRootViewControllerAnimated:NO]; }); } } else { /* Case 3: While in regular width, we are on a tab greater than the 4th and are changing to compact width. We are going INTO compact */ MoreTabDLog(@"-- going INTO compact"); if ([selectedNavControllerStack count] > 0) { [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { // no op } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { UIViewController *parentViewControllerOfTopVC = [[selectedNavControllerStack lastObject] parentViewController]; MoreTabDLog(@"parentViewControllerOfTopVC: %@", parentViewControllerOfTopVC); if ([parentViewControllerOfTopVC isKindOfClass:[UINavigationController class]] && [NSStringFromClass([parentViewControllerOfTopVC class]) hasPrefix:@"UIMore"]) { UINavigationController *moreNavigationController = (UINavigationController *)parentViewControllerOfTopVC; NSArray *moreNavigationControllerStack = [moreNavigationController viewControllers]; BOOL isOriginalRootVCInMoreStack = [moreNavigationControllerStack containsObject:[selectedNavControllerStack firstObject]]; MoreTabDLog(@"moreNav: %@, moreNavStack: %@, isOriginalRootVCInMoreStack: %i", moreNavigationController, moreNavigationControllerStack, isOriginalRootVCInMoreStack); if (!isOriginalRootVCInMoreStack) { NSArray *fixedMoreStack = [@[moreNavigationControllerStack[0]] arrayByAddingObjectsFromArray:selectedNavControllerStack]; MoreTabDLog(@"fixedMoreStack: %@", fixedMoreStack); [selectedNavController setViewControllers:selectedNavControllerStack animated:NO]; dispatch_async(dispatch_get_main_queue(), ^{ [moreNavigationController setViewControllers:fixedMoreStack animated:NO]; }); } } }]; } } } } } [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator]; MoreTabDLog(@"-- after willTransitionToTraitCollection"); } 

请享用!

约翰内斯

我find了一个解决方法,似乎解决了这个问题。

通过重写UITabBarController子类中的UITraitCollection ,您可以强制horizontalSizeClass始终为UIUserInterfaceSizeClassCompact 。 这将使UITabBar只有5个项目,不pipe方向。

这里有一些示例Objective-C代码:

 - (UITraitCollection *)traitCollection { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { // Workaround to fix the iPhone 6 Plus roatation issue. UITraitCollection *curr = [super traitCollection]; UITraitCollection *compact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact]; return [UITraitCollection traitCollectionWithTraitsFromCollections:@[curr, compact]]; } return [super traitCollection]; } 

然后,如果你需要访问真正的特征,那么在你的UIViewController覆盖-traitCollection ,从[UIScreen mainScreen]返回特征。

这里有一些例子Objective-C代码来做到这一点:

 - (UITraitCollection *)traitCollection { return [UIScreen mainScreen].traitCollection; } 

这不是一个理想的解决scheme,但直到苹果决定解决这个错误,这将做的工作。

我希望这可以帮助别人。

rdar:// 21297168