UINabigationController里面的一个UITabBarController里面的UISplitViewController在iPhone上以模态方式呈现

时间:2014-09-14 09:21:32

标签: iphone uinavigationcontroller uitabbarcontroller ios8 uisplitviewcontroller

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

Storyboard

在iPad上按预期工作。 show detail segue 在详细视图上的导航控制器中显示imageview。

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

从故事板中删除UITabBarController并直接在主视图中使用UINavigationController时,这可以正常工作。

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

5 个答案:

答案 0 :(得分:9)

彼得的解决方案的问题在于它会因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 +横向上正确更新详细视图。

答案 1 :(得分:8)

我想出了如何将详细信息放到master的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;
}

答案 2 :(得分: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讨论了.pushViewController.showViewController之间差异的评论。

Apple文档说:

  

showViewController:发送器:

     

此方法推送新的视图控制器   以类似的方式进入导航堆栈   pushViewController:animated:方法。您可以直接调用此方法   如果你想要,但通常这个方法是从其他地方调用的   在需要新视图控制器时查看控制器层次结构   所示。

     

适用于iOS 8.0及更高版本。

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

我在Swift中实现了@Dreaming In Binary的答案:

func splitViewController(splitViewController: UISplitViewController, showDetailViewController vc: UIViewController, sender: AnyObject?) -> Bool {
    let masterVC = splitViewController.viewControllers[0] as UITabBarController

    if splitViewController.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.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
}