【问题标题】:UINavigationController inside a UITabBarController inside a UISplitViewController presented modally on iPhoneUISplitViewController 内的 UITabBarController 内的 UINavigationController 以模态方式呈现在 iPhone 上
【发布时间】:2014-11-08 00:32:31
【问题描述】:

我有一个 UISplitViewController,其中包含一个 UITabBarController 作为主视图。 这个 UITabBarController 包含一个 UINavigationController。详细视图也包含一个 UINavigationController。

在 iPad 上,这可以正常工作。 show detail segue 在详细视图上显示导航控制器内的图像视图。

另一方面,在 iPhone 上,我希望 show detail segue 将详细视图推送到主视图的导航控制器堆栈上。但实际上它是在主视图上模态呈现的。

当从情节提要中移除 UITabBarController 并直接在主视图中使用 UINavigationController 时,这是可行的。

有人知道如何在 iPhone 上的主 UINavigationController 堆栈上显示详细视图吗?

【问题讨论】:

    标签: iphone uinavigationcontroller uitabbarcontroller ios8 uisplitviewcontroller


    【解决方案1】:

    我在 Swift 中实现了@Dreaming In Binary's answer:

    func splitViewController(splitViewController: UISplitViewController, showDetailViewController vc: UIViewController, sender: AnyObject?) -> Bool {
        let masterVC = splitViewController.viewControllers[0] as UITabBarController
    
        if splitViewController.traitCollection.horizontalSizeClass == .Compact {
            masterVC.selectedViewController?.showViewController(vc, sender: sender)
        } else {
            splitViewController.viewControllers = [masterVC, vc]
        }
    
        return true
    }
    
    func splitViewController(splitViewController: UISplitViewController, separateSecondaryViewControllerFromPrimaryViewController primaryViewController: UIViewController!) -> UIViewController? {
        let masterVC = splitViewController.viewControllers[0] as UITabBarController
    
        if let navController = masterVC.selectedViewController as? UINavigationController {
            if navController.viewControllers.count > 1 {
                return navController.popViewControllerAnimated(false)
            }
        }
        return nil
    }
    

    【讨论】:

      【解决方案2】:

      当我实现完全相同的 UI 结构应用程序时,我很欣赏这个讨论线程,并且进一步使其适应 iPhone 6 Plus 旋转和 iPad 多任务处理(Slide Over/Split View,iOS 9 或更高版本)。

      我们已经在 GitHub indievox-inc/TabBarSplitViewController 上开源了完整的解决方案(以 UITabBarController 作为主视图控制器的自适应 UISplitViewController)。谢谢!

      【讨论】:

        【解决方案3】:

        Peter 的解决方案的问题是它会随着 iPhone 6+ 的出现而崩溃。 怎么会呢?使用该代码,如果 iPhone 6 + 处于纵向 - 详细视图会推送到导航堆栈上。一切都很好,到目前为止。现在,旋转成横向,然后您将看到详细视图显示为详细视图主视图。

        您需要拆分视图控制器的委托来实现两种方法:

        - (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)detailVC sender:(id)sender
        {
            UITabBarController *masterVC = splitViewController.viewControllers[0];
        
            if (splitViewController.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact)
                [masterVC.selectedViewController showViewController:detailVC sender:sender];
            else
                [splitViewController setViewControllers:@[masterVC, detailVC]];
        
            return YES;
        }
        

        现在,您需要从选定选项卡的导航控制器返回顶部视图控制器:

        - (UIViewController*)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController
        {
            UITabBarController *masterVC = splitViewController.viewControllers[0];
        
            if ([(UINavigationController*)masterVC.selectedViewController viewControllers].count > 1)
                return [(UINavigationController*)masterVC.selectedViewController popViewControllerAnimated:NO];
            else
                return nil; // Use the default implementation
        }
        

        使用此解决方案,所有内容都会在应有的时候推送到导航堆栈,并且还会在 iPad/6+ 横向上正确更新详细视图。

        【讨论】:

        • 你的实现 - (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)detailVC sender:(id)sender 对我来说似乎已经足够了。
        【解决方案4】:

        @PeterOettl 对他自己的问题的回答让我走上了正确的道路,并且非常适合。所以功劳属于他。

        我的故事板结构与他几乎相同,但由于vcnavigationController,我收到运行时错误提示

        '不支持推送导航控制器'

        如前所述,那是因为vc 是详细视图的navigationController,而不是详细视图的 viewController。

        请注意,我很惊讶@PeterOettl 在他的情况下也没有收到该错误,因为故事板图片中给出的 segue 指向详细视图的导航控制器。

        因此代码应该像这样(在 Swift 中)只需添加

        let detailViewControllerNavigationController = (vc as UINavigationController).viewControllers[0] as UIViewController
        

        并推送detailViewControllerNavigationController 而不是vc

        整个代码是

        func splitViewController(splitViewController: UISplitViewController, showDetailViewController vc: UIViewController, sender: AnyObject?) -> Bool {
            println("UISplitViewController collapsed: \(splitViewController.collapsed)")
            if (splitViewController.collapsed) {
                let master = splitViewController.viewControllers[0] as UITabBarController
                let masterNavigationController = master.selectedViewController as UINavigationController
        
                let detailViewControllerNavigationController = (vc as UINavigationController).viewControllers[0] as UIViewController
        
                masterNavigationController.pushViewController(detailViewControllerNavigationController, animated: true)
        
                return true
            } else {
                return false
            }
        }
        

        另请注意,此代码放在 Xcode 主从示例的 AppDelegate.swift 中,其中在主视图中添加了一个标签栏。

        编辑

        在我们与 @PeterOettl 讨论的 cmets 中,.pushViewController.showViewController 之间的区别。

        Apple 文档说:

        showViewController:sender:

        这个方法推送一个新的视图控制器 以与 pushViewController:animated: 方法。可以直接调用这个方法 如果你愿意,但通常这个方法是从其他地方调用的 需要新视图控制器时的视图控制器层次结构 显示。

        适用于 iOS 8.0 及更高版本。

        【讨论】:

        • 这真的很有趣!在上面写我的解决方案时,我只从我的故事板上实现了上面的显示细节segue。现在在实现较低的显示详细信息 segue 时,我收到了与您一样的错误(“不支持推送导航控制器”)。我无法弄清楚这两个 segue 之间的任何区别,但是您的提示效果很好。感谢那。无论如何,找出为什么它在第一个 segue 中有效,但在第二个中无效,这将是非常有趣的。如果有人对此有想法,那就太好了。
        • 我使用了 showViewController 方法而不是 pushViewController。这在这两种情况下都很有效,即使我仍然不确定两者之间的差异,因为我两次都在 UINavigationController 上推送 UINavigationController。你可以试试 showViewController 是否也适用于你的情况?
        • 抱歉耽搁了。我尝试使用 .showViewController 而不是 .pushViewController ,和你一样,我没有看到任何差异。动画看起来一样,一切看起来都一样。我在问自己框架词是否标识它显示导航控制器并因此自动推送显示。
        • 我找到了答案。 Apple 文档说: showViewController:sender: 这个方法将一个新的视图控制器推送到导航堆栈上,其方式与 pushViewController:animated: 方法类似。如果需要,您可以直接调用此方法,但通常当需要显示新的视图控制器时,会从视图控制器层次结构中的其他位置调用此方法。适用于 iOS 8.0 及更高版本。
        【解决方案5】:

        我想出了如何将细节放在主人的 UINavigationController 上,而不是在 UITabBarController 上模态地呈现它。

        使用 UISplitViewControllerDelegate 方法

        - splitViewController:showDetailViewController:sender:
        

        如果 UISplitViewController 折叠,获取主导航控制器并将详细视图推送到此导航控制器:

        - (BOOL)splitViewController:(UISplitViewController *)splitViewController
           showDetailViewController:(UIViewController *)vc
                             sender:(id)sender {
            NSLog(@"UISplitViewController collapsed: %d", splitViewController.collapsed);
        
            // TODO: add introspection
            if (splitViewController.collapsed) {
                UITabBarController *master = (UITabBarController *) splitViewController.viewControllers[0];
                UINavigationController *masterNavigationController = (UINavigationController *)master.selectedViewController;
        
                // push detail view on the navigation controller
                //[masterNavigationController pushViewController:vc animated:YES];
                // push was not always working (see discussion in answer below), use showViewController instead
                [masterNavigationController showViewController:vc sender:sender];
        
                return YES;
            }
        
            return NO;
        }
        

        【讨论】:

        • 我有完全相同的故事板和问题,你的回答让我离目标更近了一点。但是, splitViewController:showDetailViewController:sender: 委托只为我触发一次......随后的“显示”不会触发委托。你最终分配了什么作为代表?
        • 进一步调查显示,我的 splitView 委托在导航后被重新分配。如果我弄清楚为什么我会更新。
        • 此解决方案不适用于 iPhone 6 +。纵向启动您的应用程序,转到详细视图。然后切换到横向,您的详细视图和主视图都将显示详细视图。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-09
        • 2010-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-13
        • 1970-01-01
        相关资源
        最近更新 更多