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

时间:2015-11-05 00:08:13

标签: ios objective-c iphone uinavigationcontroller uitabbarcontroller

我正在研究我的某个应用程序的更新,我遇到了一个与UITabBarController有关的非常奇怪的问题。

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

如果您选择纵向视图6并且当视图在标签栏中获得自己的按钮时旋转UINavigationController变黑,但是当它返回到“更多”时,视图会返回。在我对它们的研究中,似乎UINavigationController以其根视图控制器丢失了UIViewController。

在未进入“更多”标签的视图上按预期工作:imgur.com/gVB8wTF

如果视图来自“更多”标签,则显示黑屏:http://imgur.com/WaoNoL1

我制作了一个包含此问题的快速示例项目:https://github.com/joshluongo/UITabBarController-Issues

有关如何解决此问题的任何想法?

2 个答案:

答案 0 :(得分:2)

我遇到了同样的问题。

我能够提出一个非常有效的解决方法。我已经把它推到了Github:https://github.com/jfahrenkrug/UITabBarControllerMoreBugWorkaround

欢迎任何改进。

发生错误是因为UINavigationController的堆栈已从其中删除并放入私有的UIMoreNavigationController中。但是在旋转回常规宽度时,该堆栈没有正确地放回原来的UINavigationViewController。

解决方案是将UITabBarController子类化并用它替换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");
}

享受!

约翰

答案 1 :(得分:1)

我找到了解决这个问题的解决方法。

通过覆盖UITraitCollection子类中的UITabBarController,您可以强制horizontalSizeClass始终为UIUserInterfaceSizeClassCompact。这将使UITabBar只有5个项目,无论方向如何。

这里有一些示例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];
}

然后,如果您需要访问真实特征,请覆盖-traitCollection中的UIViewController,以便从[UIScreen mainScreen]返回特征。

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

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

这不是一个理想的解决方案,但在Apple决定修复此错误之前,这将完成这项工作。

我希望这有助于某人。

rdar:// 21297168