IOS8 SplitVC + TabBarController + NavigationController

Posted

技术标签:

【中文标题】IOS8 SplitVC + TabBarController + NavigationController【英文标题】: 【发布时间】:2015-07-09 22:44:24 【问题描述】:

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

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

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

在 AppDelegate.m 中添加了此代码(如此处所示 *** 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 Plus 中进行模拟,在这种情况下,在纵向开始并选择一行之后,如果您横向旋转,我会将详细视图视为主要视图和次要视图。

如果不使用 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 仍然是上一个标签),然后纵向旋转。

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

长版(SplitViewController 行为)

通常拆分视图控制器和导航控制器工作 一起确保调用 -showDetailViewController:sender: 从包含在拆分视图中的视图控制器 控制器导致新的细节视图控制器被推送到 导航堆栈(在水平紧凑的环境中)。到 这样做, UISplitViewController 覆盖 -showDetailViewController:sender: 并且,如果水平紧凑,则调用其主视图控制器的 -showViewController:sender: 方法。 UINavigationController 覆盖 -showViewController:sender: 和 将传入的视图控制器推送到导航堆栈上。

UITabBarController 但是不会覆盖 -showViewController:sender: 因此它继承了默认实现,以模态方式呈现传入的视图控制器。 要解决这个问题,我必须继承 UITabBarController 并覆盖 -showViewController:sender: 如果 selectedViewController 是导航,则转发到标签栏控制器的 selectedViewController 控制器。

此外,当拆分视图控制器从紧凑型 到水平尺寸等级到常规水平尺寸等级,拆分 视图控制器首先发送一个 -splitViewController:separateSecondaryViewControllerFromPrimaryViewController: 消息给它的代表。委托可以实现这个方法并且 处理分离本身,返回详细视图控制器。 如果委托没有实现这个方法,或者如果 实现返回 nil,拆分视图控制器发送一个 -separateSecondaryViewControllerForSplitViewController:向其主视图控制器发送消息。主视图控制器应该实现 这种方法来处理分离。 UINavigationController 确实 实现-separateSecondaryViewControllerForSplitViewController:。 它的实现将顶部视图控制器从导航中弹出 堆栈并返回它。因为我使用标签栏控制器作为 主视图控制器,我必须实现 -separateSecondaryViewControllerForSplitViewController: 并自行处理分离。

我还需要实现自己的折叠逻辑。当拆分视图 控制器从常规尺寸类转换为水平尺寸类 紧凑的水平尺寸类,拆分视图控制器首先发送一个 -splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: 消息给它的代表。委托可以实现这个方法并且 处理崩溃本身。如果委托没有实现这个 方法,拆分视图控制器发送一个 -collapseSecondaryViewController:forSplitViewController: 向其主视图控制器发送消息。主视图控制器应该 实现这个方法来处理分离。

UINavigationController 确实实现了 -collapseSecondaryViewController:forSplitViewController:。它的实现将辅助视图控制器推到 导航堆栈。因为我使用标签栏控制器作为 主视图控制器,我必须实现 -collapseSecondaryViewController:forSplitViewController: 并自行处理折叠。

【问题讨论】:

【参考方案1】:

所以,我发现了一些可行的方法,即使不是标准行为:

- (void)collapseSecondaryViewController:(UIViewController *)secondaryViewController forSplitViewController:(UISplitViewController *)splitViewController

    [self.selectedViewController.navigationController collapseSecondaryViewController:secondaryViewController forSplitViewController:splitViewController];

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

【讨论】:

【参考方案2】:

试试这个 sn-p 并告诉我们你的结果。这个 sn-p 从 *** 之外的网站获取 (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

【讨论】:

它不起作用,同样的问题。你试过了吗?它对你有用吗? 你好@Zag,对不起,我忘记了一个水平计数器。我不知道为什么,但有时(在将肖像更改为土地或反之亦然之后)我们有 3 个屏幕而不仅仅是 2 个,我想这只是为了内部操作到鞋屏幕的顺序。上面的代码对我有用。 嗨@Beto,AFAIK SplitViewController 层次结构会根据折叠或分离模式而变化。见this article。我会试试你的解决方案。谢谢。 希望对您有所帮助,如果您发现更好的东西,请写在这里。

以上是关于IOS8 SplitVC + TabBarController + NavigationController的主要内容,如果未能解决你的问题,请参考以下文章

在 splitViewController 的 detailView 中设置 barButtonItem 的标题

UINavigationController 在标签栏控制器之前和标签栏控制器之后?

iOS8开发之iOS8的UIAlertController

iOS8 MGSplitViewController 替代品

未调用 iOS8 接口旋转方法

iOS8 - 请求远程推送通知