IOS8 SplitVC + TabBarController + NavigationController

IOS8 SplitVC + TabBarController + NavigationController

我正在使用大小 类 制作一个通用应用程序,我正在尝试在 Master/Primary 视图中使用带有 TabBarController 的 SplitView。在添加 splitView 之前一切正常,但现在 App 崩溃了(原因取决于视图的层次结构)。

所以我尝试了从 Apple SplitView 模板开始的相同故事板,并在其 Master/primary 视图上添加了一个 TabBarController...同样的问题。

层次结构 - TabBarController 中的嵌入式主 NavigationController: SplitVC (Master) > TabBarController > NavigationController > TableView SplitVC(详细信息)> NavigationController > View

在 AppDelegate.m 中添加了此代码(如此处所示 Whosebug questions ios8-tabbarcontroller... 以防止以模态方式显示 DetailView):

- (BOOL)splitViewController:(UISplitViewController *)splitViewController showDetailViewController:(UIViewController *)vc sender:(id)sender {
        NSLog(@"UISplitViewController collapsed: %d", splitViewController.collapsed);

    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
    {
        if (splitViewController.collapsed) {
            UITabBarController *master = (UITabBarController *) splitViewController.viewControllers[0];
            UINavigationController *masterNavigationController = (UINavigationController *)master.selectedViewController;
            UINavigationController *destinationNavigationController = (UINavigationController *)vc;

            // push detail view on the navigation controller
            [masterNavigationController pushViewController:[destinationNavigationController.viewControllers lastObject] animated:YES];

            return YES;
        }
    }

    return NO;
}

它工作正常...除非你在 iPhone6 中模拟另外,在那种情况下,在纵向开始并选择一行后,如果你在横向旋转我看到详细视图作为主要和次要视图.

如果不在 iPhone 的纵向模式下添加此代码,详细视图将以模态方式呈现,当然也没有导航按钮。

编辑

经过多次尝试并借助一些外部帮助,我已经朝着解决方案迈出了一些步伐。

短版(请参阅长版了解为什么必须这样做)

问题的正确解决方案是继承 TabBarController 并使其支持一些方法:

@implementation MyTabBarController

- (void)showViewController:(UIViewController *)vc sender:(id)sender
{
    if ([self.selectedViewController isKindOfClass:UINavigationController.class])
        [self.selectedViewController showViewController:vc sender:sender];
    else
        [super showViewController:vc sender:sender];
}

- (UIViewController*)separateSecondaryViewControllerForSplitViewController:(UISplitViewController *)splitViewController
{
    return [self.selectedViewController separateSecondaryViewControllerForSplitViewController:splitViewController];
}

- (void)collapseSecondaryViewController:(UIViewController *)secondaryViewController forSplitViewController:(UISplitViewController *)splitViewController
{
    [self.selectedViewController collapseSecondaryViewController:secondaryViewController forSplitViewController:splitViewController];
}

现在我遇到了 viewControllers 堆栈的问题:使用 iPhone6Plus(唯一支持水平常规和紧凑的应用程序)如果在横向模式下更改选项卡而不选择行(所以 detailView 仍然是前一个选项卡的那个)然后纵向旋转。

我知道我必须实施分离和折叠方法来正确管理视图堆栈,但我不知道如何操作。有人可以帮忙吗?

Long version (SplitViewController behaviour)

Normally a split view controller and a navigation controller work together to ensure that a call to -showDetailViewController:sender: from a view controller that is contained within the split view controller results in the new detail view controller being pushed onto the navigation stack (when in a horizontally compact environment). To do this, UISplitViewController overrides -showDetailViewController:sender: and, if horizontally compact, calls its master view controller's -showViewController:sender: method. UINavigationController overrides -showViewController:sender: and pushes the incoming view controller onto the navigation stack.

UITabBarController however does not override -showViewController:sender: and so it inherits the default implementation which presents the incoming view controller modally. To work around this I have to subclass UITabBarController and override -showViewController:sender: to forward to the tab bar controller's selectedViewController if the selectedViewController is a navigation controller.

Furthermore, when a split view controller transitions from a compact to horizontal size class to a regular horizontal size class, the split view controller first sends a -splitViewController:separateSecondaryViewControllerFromPrimaryViewController: message to its delegate. The delegate can implement this method and handle the separation itself, returning the detail view controller. If the delegate does not implement this method, or if the implementation returns nil, the split view controller sends a -separateSecondaryViewControllerForSplitViewController: message to its primary view controller. The primary view controller should implement this method to handle the separation. The UINavigationController does implement -separateSecondaryViewControllerForSplitViewController:. It's implementation pops the top view controller off the navigation stack and returns it. Because I am using a tab bar controller as the primary view controller, I must implement -separateSecondaryViewControllerForSplitViewController: and handle the separation by myself.

Also I need to implement my own collapsing logic. When a split view controller transitions from a regular to horizontal size class to a compact horizontal size class, the split view controller first sends a -splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: message to its delegate. The delegate can implement this method and handle the collapse itself. If the delegate does not implement this method, the split view controller sends a -collapseSecondaryViewController:forSplitViewController: message to its primary view controller. The primary view controller should implement this method to handle the separation.

UINavigationController does implement -collapseSecondaryViewController:forSplitViewController:. It's implementation pushes the secondary view controller onto the navigation stack. Because I am using a tab bar controller as the primary view controller, I must implement -collapseSecondaryViewController:forSplitViewController: and handle the collapse by myself.

试试这个片段并告诉我们你的结果。此代码段来自 Whosebug 之外的网站 (Craig Marvelley)

#pragma mark - Split view
// Update secondaryview with the right screen
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController { 
int tryIt = 0;

if ((IS_IPHONE_6_PLUS) && (isLandscape)) {
    if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
        for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
            tryIt = tryIt + 1;
            if ([controller isKindOfClass:[UINavigationController class]] && ([[(UINavigationController *)controller visibleViewController] isKindOfClass:[yourPosibleScreen01 class]] || [[(UINavigationController *)controller visibleViewController] isKindOfClass:[yourPosibleScreen02 class]]) ) {
                return controller;
            }
            // Sublevel where yo are to select the right screen. You must try with a number depends of how many internal hierarchy. But I believe you need number 2 but try it :) 
            if (tryIt > 2) {
                return controller;
            }
        }
    }
    // Update detail screen
    UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"YourScreenToShow"];
    return toViewController;
}
return nil;
}


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

     return NO;
}
#pragma mark - Split view

所以,我发现了一些有用的东西,即使这不是标准行为:

- (void)collapseSecondaryViewController:(UIViewController *)secondaryViewController forSplitViewController:(UISplitViewController *)splitViewController
{
    [self.selectedViewController.navigationController collapseSecondaryViewController:secondaryViewController forSplitViewController:splitViewController];
}

相当于return总是YESsplitViewController:collapseSecondaryViewController:ontoPrimaryViewController:委托方法中。像这样,您总是丢弃辅助控制器。 希望这可以帮助某人。