【问题标题】:Having a UINavigationController in the master view of a UISplitViewController in iOS 8在 iOS 8 中 UISplitViewController 的主视图中有一个 UINavigationController
【发布时间】:2014-09-26 13:35:21
【问题描述】:

在我的UISplitViewController 中,主视图是一个包含UITableViewControllerUINavigationController。有时,当用户在表中选择一个项目时,我必须在主视图中的现有表上推送另一个 UITableViewController

在 iOS 7 中,在我的第一个 UITableViewController 中,我只是调用

[self.navigationController pushViewController:otherTableVC animated:YES];

在 iOS 8 中:

当拆分视图折叠时,otherTableVC 变成了详细视图! 然后旋转设备后,我们看到两张桌子并排...

更糟糕的是,如果设备显示两个窗格,则代码运行良好,并且第二个表格在主视图中被推到第一个表格之上。但是,在两次旋转之后,两张桌子又是并排的。 UISplitViewController 的折叠模式似乎干扰了我自己的导航控制器……

如何在主视图中管理我自己的UINavigationController

谢谢

已编辑:

我的主要视图和细节视图都有一个导航控制器。为了解决我的问题,我刚刚发现,在折叠模式下,我必须创建一个额外的导航控制器并将其推送到主导航控制器上。

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:otherTableVC];
[self.navigationController pushViewController:navController animated:YES];

所以我刚刚发现我们可以将导航控制器推入另一个导航控制器。

【问题讨论】:

    标签: uinavigationcontroller ios8 uisplitviewcontroller


    【解决方案1】:

    简短的回答,您可以通过 UISplitViewControllerDelegate 方法控制此行为:

    splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
    splitViewController:separateSecondaryViewControllerFromPrimaryViewController:
    

    我怀疑你真正想要做的是处理你有一个基于 iOS 8 UISplitViewController 的应用程序的情况,其中你的主要和详细视图都是 UINavigationControllers 并且有一些你想要的 viewControllers(在这些导航控制器中)仅出现在拆分视图的主要或详细信息侧。下面的答案涉及到这个问题。它还可以处理您有时希望视图替换 Detail 导航控制器中的视图,而不是被推送到那里的情况。

    一个小警告:下面的代码并没有处理所有可能的情况,并且有一些假设:

    • 我们预计,当拆分视图折叠并且这些视图被其上方的详细视图遮挡时,详细导航控制器堆栈上不会发生任何变化。
    • 我们的 UIViewController 子类都有一个 shouldDisplayInDetailedView 和 shouldReplaceDetailedView 属性
    • 我们假设我们只将视图推送到设置了 shouldDisplayInDetailedView 属性的详细导航控制器上。
    • 视图控制器通过 splitViewController:showDetailViewController: 或 pushViewController:animated: 在详细视图中的视图的 navigationController 属性上添加到详细信息端(处于展开或折叠状态)。
    • 应该替换 Detail 导航控制器中的视图控制器的视图控制器只能通过 splitViewController:showDetailViewController: 添加,并且只能通过与主视图控制器中的视图交互,即,只有在以下情况下才会发生这种情况处于折叠状态时,主视图控制器不会被遮挡。
    • 当拆分视图控制器展开时,我们有一个 BlankViewController 显示在详细视图中,但我们只有应保留在主端的视图控制器。

    我不建议只实现 splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: / splitViewController: separateSecondaryViewControllerFromPrimaryViewController: 逻辑的一侧,并取决于另一侧的默认实现。 Apple 做了一些奇怪的事情,比如将 UINavigationViewController 从 Detail 端放入主端,作为主导航控制器堆栈中的 viewControllers 之一,然后将其他视图控制器推到它上面,即使你完全理解仍然无法复制你自己的代码。因此,最好自己处理流程的双方。

    这是我用的:

    #pragma mark -
    #pragma mark Split View Controller delegate.
    
    - (BOOL)splitViewController:(UISplitViewController *)splitViewController showViewController:(UIViewController *)vc sender:(id)sender
    {
        //Standard behaviour.  This won't get called in our case when the split view is collapsed and the primary view controllers are obscured.
        return NO;
    }
    
    // Since we treat warnings as errors, silence warning about unknown selector below on UIViewController subclasses.
    #pragma GCC diagnostic ignored "-Wundeclared-selector"
    
    
    - (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)vc sender:(id)sender
    {
        if (splitViewController.collapsed == NO)
        {
            // The navigation controller we'll be adding the view controller vc to.
            UINavigationController *navController = splitViewController.viewControllers[1];
    
            UIViewController *topDetailViewController = [navController.viewControllers lastObject];
            if ([topDetailViewController isKindOfClass:[BlankViewController class]] ||
               ([vc respondsToSelector:@selector(shouldReplaceDetailedView)] && [vc performSelector:@selector(shouldReplaceDetailedView)]))
            {
                // Replace the (expanded) detail view with this new view controller.
                [navController setViewControllers:@[vc] animated:NO];
            }
            else
            {
                // Otherwise, just push.
                [navController pushViewController:vc animated:YES];
            }
        }
        else
        {
            // Collapsed.  Just push onto the conbined primary and detailed navigation controller.
            UINavigationController *navController = splitViewController.viewControllers[0];
            [navController pushViewController:vc animated:YES];
        }
    
        // We've handled this ourselves.
        return YES;
    }
    
    - (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController
    {
        UINavigationController *primaryNavController = (UINavigationController *)primaryViewController;
        UINavigationController *secondaryNavController = (UINavigationController *)secondaryViewController;
        UIViewController *bottomSecondaryView = [secondaryNavController.viewControllers firstObject];
        if ([bottomSecondaryView isKindOfClass:[BlankViewController class]])
        {
            NSAssert([secondaryNavController.viewControllers count] == 1, @"BlankViewController is not only detail view controller");
            // If our secondary controller is blank, do the collapse ourself by doing nothing.
            return YES;
        }
    
        // We need to shift these view controllers ourselves.
        // This should be the primary views and then the detailed views on top.
        // Otherwise the UISplitViewController does wacky things like embedding a UINavigationController inside another UINavigation Controller, which causes problems for us later.
        NSMutableArray *newPrimaryViewControllers = [NSMutableArray arrayWithArray:primaryNavController.viewControllers];
        [newPrimaryViewControllers addObjectsFromArray:secondaryNavController.viewControllers];
        primaryNavController.viewControllers = newPrimaryViewControllers;
    
        return YES;
    }
    
    - (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController
    {
        UINavigationController *primaryNavController = (UINavigationController *)primaryViewController;
    
        // Split up the combined primary and detail navigation controller in their component primary and detail view controller lists, but with same ordering.
        NSMutableArray *newPrimaryViewControllers = [NSMutableArray array];
        NSMutableArray *newDetailViewControllers = [NSMutableArray array];
        for (UIViewController *controller in primaryNavController.viewControllers)
        {
            if ([controller respondsToSelector:@selector(shouldDisplayInDetailedView)] && [controller performSelector:@selector(shouldDisplayInDetailedView)])
            {
                [newDetailViewControllers addObject:controller];
            }
            else
            {
                [newPrimaryViewControllers addObject:controller];
            }
        }
    
        if (newDetailViewControllers.count == 0)
        {
            // If there's no detailed views on the top of the navigation stack, return a blank view  (in navigation controller) for detailed side.
            UINavigationController *blankDetailNavController = [[UINavigationController alloc] initWithRootViewController:[[BlankViewController alloc] init]];
            return blankDetailNavController;
        }
    
        // Set the new primary views.
        primaryNavController.viewControllers = newPrimaryViewControllers;
    
        // Return the new detail navigation controller and views.
        UINavigationController *detailNavController = [[UINavigationController alloc] init];
        detailNavController.viewControllers = newDetailViewControllers;
        return detailNavController;
    }
    

    【讨论】:

    • 非常感谢迈克尔。事实上,我们只是有很多工作来处理新的“折叠”模式。我认为苹果在这里做出了一个非常糟糕的决定。为什么不保持与弹出窗口相同的原理(如 iPad 处于纵向模式)!
    • 这救了我的培根。我在使用 iOS 9 和多任务功能时遇到了问题。手动指定所有这些东西真的很有帮助!
    • 我也在努力解决同样的问题。你们中的一个人可以上传一个带有上述设置的小项目吗?我尝试转换为 Swift,如果我在详细视图控制器中设置 shouldDisplayInDetailedView,我会收到“不支持推送导航控制器”消息。
    • 有 Swift 版本吗?
    【解决方案2】:

    Swift 4 版本稍作改动以使其适用于我的代码:

    func splitViewController(_ splitViewController: UISplitViewController, showDetail vc: UIViewController, sender: Any?) -> Bool {
        if !isCollapsed {
            // in expanded mode set new VC as top view controller of the detail nav controller
            if let detailNavigationController = viewControllers[1] as? UINavigationController {
               detailNavigationController.setViewControllers([vc], animated: false)
            }
        } else {
            // in collapsed mode push the new view controller on the master nav controller
            if let masterNavigationController = viewControllers[0] as? UINavigationController {
                masterNavigationController.pushViewController(vc, animated: true)
            }
        }
        return true
    }
    
    func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
    
        let masterNavigationController = primaryViewController as? UINavigationController
        let detailNavigationController = secondaryViewController as? UINavigationController
        let episodeDetailViewController = detailNavigationController?.viewControllers.first as? EpisodeDetailTableViewController
        if episodeDetailViewController?.episode == nil {
            // detail view is blank. We do not need to push this onto the master
            return true
        }
    
        guard var newMasterViewControllers = masterNavigationController?.viewControllers else { return false }
        newMasterViewControllers.append(contentsOf: detailNavigationController?.viewControllers ?? [])
        masterNavigationController?.setViewControllers(newMasterViewControllers, animated: false)
        return true
    }
    
    
    func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
        let masterNavigationViewController = primaryViewController as? UINavigationController
    
        var newMasterViewControllers = [UIViewController]()
        var newDetailViewControllers = [UIViewController]()
    
        for vc in masterNavigationViewController?.viewControllers ?? [] {
            if vc is PodcastsTableViewController || vc is EpisodesTableViewController {
                newMasterViewControllers.append(vc)
            } else {
                newDetailViewControllers.append(vc)
            }
        }
    
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let detailNavigationController = storyboard.instantiateViewController(withIdentifier: "splitViewDetailViewController") as! UINavigationController
    
        if newDetailViewControllers.count == 0 {
            let emptyEpisodeDetailViewController = storyboard.instantiateViewController(withIdentifier: "episodeDetail")
            newDetailViewControllers.append(emptyEpisodeDetailViewController)
        }
    
        masterNavigationViewController?.setViewControllers(newMasterViewControllers, animated: false)
        detailNavigationController.setViewControllers(newDetailViewControllers, animated: false)
        return detailNavigationController
    }
    

    【讨论】:

    • 非常感谢!也适用于 Swift 5!
    猜你喜欢
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    相关资源
    最近更新 更多