UISplitViewController 的详情视图推送错误

UISplitViewController's detail view push error

我正在为 iOS8 使用 UISplitViewController 实现一个通用应用程序,但遇到 UINavigation 的奇怪问题,非常感谢您的专业知识。

我的项目有以下 StoryBoard 布局:

在 iPad,一切正常。但是,iPhone 上的 运行 导航没有按预期工作。当我从 "Detail Screen 2" 导航回 "Detail Screen 1" 时,请查看此 short video 演示导航问题。

我尝试在一个全新的项目上实现同样的场景,但我没有发现问题。只有移植到我现有的项目后,我才会看到这种行为。

更新 1:

这是我的 AppDelegate 代码:

    @interface AppDelegate () <UISplitViewControllerDelegate>

    @end

    @implementation AppDelegate

    -(BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions {

        UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
        UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
        navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
        splitViewController.delegate = self;

        splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;

        return YES;
    }

    #pragma mark - Split view

    - (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {

        return YES;
    }

....
@end

更新 2:

感谢 PetahChristian,我认为他的以下观察是正确的:

To collapse a secondary view controller which has a navigation controller, Apple inserts the secondary navigation controller onto the primary navigation controller's stack. So, for the iPhone, where you see the problems, it looks like there is only one navigation controller, but there actually are two.

假设观察是正确的,如何防止副导航控制器推到主导航控制器上? UISplitViewControllerDelegate 方法仅处理直接链接到 UISplitViewController 的辅助视图控制器的折叠逻辑。在我的例子中,要折叠的辅助视图控制器(即 Detail VC1)通过“Show Detail(例如 Replace)”segue 从主视图控制器和 UISplitViewControllerDelegate 方法在此转换期间不执行。

在全新项目中使用完全相同的设置,Apple 不会将辅助导航控制器插入主导航控制器,我在新项目中也没有遇到此问题。

非常感谢。

项目之间的行为不同,因为 UISplitViewControllerDelegate 代码不同。

新项目有必要的代码,但现有项目可能缺少它。

检查您的 AppDelegate 并比较处理折叠和分离辅助视图控制器的代码。

-splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
-splitViewController:separateSecondaryViewControllerFromPrimaryViewController:

更新:

你不应该在 splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: 中无条件地 return YES。您需要首先确定哪个视图控制器在顶部:看看他们如何检查它是否是(辅助导航控制器及其子)详细视图控制器,并且它有要显示的详细信息?

- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
    if ([secondaryViewController isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]] && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {
        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;
    } else {
        return NO;
    }
}

更新二:

为了折叠具有导航控制器的辅助视图控制器,Apple 将辅助导航控制器插入到主导航控制器的堆栈中。所以,对于iPhone,你看到问题的地方,看起来只有一个导航控制器,但实际上有两个。

在你的视频中,当它看起来像你在主导航,但它有一个后退按钮,你点击它,后退按钮滑出屏幕,这就是辅助导航控制器从主导航中消失控制器。

为了让你的项目正常工作,你必须有条件地测试导航控制器,就像苹果所做的那样,以确定(导航控制器及其子)细节视图控制器是否被丢弃。

更新 3:

导航控制器本身就是一个视图控制器。它控制它的子视图控制器。

辅助导航控制器辅助视图控制器。详细视图控制器是它的子视图控制器。

splitView 委托方法处理的是次级导航控制器的折叠或分离,它恰好有一个或多个子视图控制器。

当 splitView 控制器折叠时,主导航控制器的堆栈看起来像 [masterViewController, secondaryNavigationController]。

至于替换segue,你要替换的是Storyboard底部的Empty detail view controller,带有橙色的Detail Screen 1。但是被替换的detail view controller仍然有一个parent,secondary navigation controller .当 splitViewController 崩溃时,辅助导航控制器最终位于主导航控制器堆栈上。你看不到这个,因为它是透明的。您所看到的只是详细信息屏幕 1,因为它是辅助导航控制器的顶视图控制器。

您的 splitViewDelegate 代码已损坏。这就是当您点击“返回”按钮时视图控制器无法在视频中正确显示动画的原因。修复委托代码,所有 正常显示和工作。

您绝对需要折叠和分离方法,并且它们需要做正确的事情。这就是为什么我建议您使用 Apple 的代码,而不是尝试编写自己的代码,因为 Apple 的代码正确地折叠并分离了导航控制器。

在我的项目中导致这种导航异常的罪魁祸首是我从 SO 用户那里下载的一个名为 UIViewController+BackButtonHandler 的扩展。此处理程序拦截导航后退按钮,因此我可以在用户按下后退时有机会做额外的工作。此类别扩展代码覆盖 navigationBar:shouldPopItem:,导致默认导航中断。我不知道这段代码正在执行,因为我没有使用它,而是只是合并到我的项目中。哇...用头撞墙2天