首页 文章

在iOS 8中的UISplitViewController的主视图中有一个UINavigationController

提问于
浏览
12

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

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

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

在iOS 8中:

折叠拆分视图时,otherTableVC将成为详细视图!旋转设备后,我们并排看到两张 table ......

更糟糕的是,如果设备显示两个窗格,代码工作得很好,第二个表被推到主视图中的第一个表 . 但是,经过两次旋转后,两张 table 又是并排的 . 似乎UISplitViewController的折叠模式干扰了我自己的导航控制器......

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

谢谢

EDITED:

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

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

所以我发现帽子我们可以将导航控制器推到另一个导航控制器中 .

2 回答

  • 24

    简而言之,您可以通过UISplitViewControllerDelegate方法控制此行为:

    splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
    splitViewController:separateSecondaryViewControllerFromPrimaryViewController:
    

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

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

    • 当拆分拆分视图并且这些视图被其上方的详细视图遮挡时,我们预计详细导航控制器堆栈上的任何内容都不会发生变化 .

    • 我们的UIViewController子类都有一个shouldDisplayInDetailedView和shouldReplaceDetailedView属性

    • 我们假设我们只将视图推送到具有shouldDisplayInDetailedView属性集的详细导航控制器 .

    • 视图控制器通过splitViewController添加到Detail端:showDetailViewController:或pushViewController:animated:在详细视图中的视图的navigationController属性上(处于展开或折叠状态) .

    • 应该替换Detail导航控制器中的视图控制器的视图控制器只能通过splitViewController:showDetailViewController添加:并且只能通过与主视图控制器中的视图的交互添加,即,只有在主视图控制器未被遮挡时才会发生这种情况 . 崩溃的国家 .

    • 当拆分视图控制器扩展时,我们有一个BlankViewController在详细信息视图中显示,但我们只有视图控制器应保留在主端 .

    我不建议只实现splitViewController的一面:collapseSecondaryViewController:ontoPrimaryViewController:/ splitViewController:separateSecondaryViewControllerFromPrimaryViewController:logic并取决于另一方的默认实现 . Apple做了一些奇怪的事情,例如将UINavigationViewController从Detail端放入主要侧作为Primary导航控制器堆栈中的一个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;
    }
    
  • 0

    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
    }
    

相关问题