【问题标题】:UITabBarController + UINavigationController and more tab black screen issueUITabBarController + UINavigationController 和更多选项卡黑屏问题
【发布时间】:2016-02-05 16:36:14
【问题描述】:

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

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

如果您选择纵向视图 6 并且当视图在选项卡栏中获得自己的按钮时旋转 UINavigationController 会变黑,但是当它返回“更多”时,视图会返回。在我对这些的调查中,似乎 UINavigationController 失去了 UIViewController 作为它的根视图控制器。

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

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

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

关于如何解决这个问题的任何想法?

【问题讨论】:

  • 你在设备上试过了吗?它适用于 iPhone5s 模拟器,但不适用于 iPhone 6s plus 模拟器。我发现了一个警告日志Unbalanced calls to begin/end appearance transitions for <UIViewController: 0x7faa186db7b0>,但它不应该是。我没有 iPhone 6s plus,所以请先在设备上测试。可能是模拟器的bug。
  • 我已经在我的 iPhone 6 Plus 上尝试过了,它得到了与模拟器相同的结果。它似乎只影响旋转时显示超过 5 个项目的设备。
  • 你有什么发现吗?我仍在努力。太奇怪了。它只发生在 iPhone 6+ 和 iPhone 6s+ 上。我怀疑size class 但不确定。
  • 目前还没有。我也在Apple Developer Forums 上发布了这个。如果我找不到解决方案,我将不得不在这个问题上使用我的TSI's 之一。当我找到解决方案时,我会更新这个帖子。
  • 我对此的最佳猜测是 UITabBarController 正在将视图控制器从 UINavigationController 中拉出并将它们放在 UIMoreNavigationController 中(因为 UINavigationController 在 UINavigationController 中是不可以的)并且没有将它们返回给他们原来的父母。

标签: ios objective-c iphone uinavigationcontroller uitabbarcontroller


【解决方案1】:

我遇到了同样的问题。

我能够想出一个非常有效的解决方法。我已经把它推到了 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");
}

享受吧!

约翰内斯

【讨论】:

    【解决方案2】:

    我找到了似乎可以解决此问题的解决方法。

    通过覆盖UITabBarController 子类中的UITraitCollection,您可以强制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];
    }
    

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

    这里有一些示例 Objective-C 代码可以做到这一点:

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

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

    我希望这对某人有所帮助。

    rdar://21297168

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多