Наличие UINavigationController в главном представлении UISplitViewController в iOS 8

В моем UISplitViewController главный вид - это UINavigationController, содержащий UITableViewController. Когда пользователь выбирает элемент в таблице, я должен нажать другой tableViewController поверх существующей таблицы в главном представлении.

В 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];

Итак, я только что открыл шляпу, мы можем нажать контроллер навигации внутри другого контроллера навигации.

Ответ 1

Короткий ответ, вы можете контролировать это поведение с помощью методов UISplitViewControllerDelegate:

splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
splitViewController:separateSecondaryViewControllerFromPrimaryViewController:

Я подозреваю, что вы действительно хотите сделать, это разобраться с ситуацией, когда у вас есть приложение на базе iOS 8 UISplitViewController, где ваши первичные и подробные представления являются как UINavigationControllers, так и некоторыми контроллерами viewControllers (в пределах этих контроллеров навигации), которые вы хотите отображаются только на основной или детальной стороне раскола. Ниже приведен ответ. Он также справляется с ситуацией, когда вы иногда хотите, чтобы представление заменяло представления в контроллере навигации Detail, а не нажималось туда.

Небольшое предостережение: приведенный ниже код не касается всех возможных случаев и имеет некоторые предположения:

  • Мы не ожидаем, что что-либо может измениться в стеке Подробного навигационного контроллера, когда сплит-представление свернуто, и эти представления затенены подробным представлением над ними.
  • В наших подклассах UIViewController есть свойство shouldDisplayInDetailedView и shouldReplaceDetailedView
  • Мы предполагаем, что мы только нажимаем представления на подробный контроллер навигации, у которого есть свойство shouldDisplayInDetailedView.
  • Контроллеры просмотра добавляются в сторону Detail через splitViewController: showDetailViewController: или pushViewController: анимированный: в свойстве navigationController представления в подробном представлении (в развернутом или свернутом состоянии).
  • Контроллеры просмотра, которые должны заменить контроллеры представлений в контроллере навигации Detail, добавляются только через splitViewController: showDetailViewController: и только из взаимодействия с представлением в контроллере Primary view, т.е. это может произойти только в том случае, если контроллер Primary view не затенен когда в сложенном состоянии.
  • У нас есть BlankViewController для отображения в подробном представлении, когда контроллер разделенного представления расширяется, но у нас есть только контроллеры представлений, которые должны оставаться на главной стороне.

Я не рекомендую применять только одну сторону splitViewController: collapseSecondaryViewController: onPrimaryViewController:/splitViewController: отдельныйSecondaryViewControllerFromPrimaryViewController: логический и в зависимости от реализации по умолчанию для другой стороны. Apple делает некоторые странные вещи, такие как включение UINavigationViewController из стороны Detail в основную сторону в качестве одного из контроллеров viewControlers в стеке контроллера первичной навигации, а затем нажатия на другие контроллеры представлений над ним, которые даже если вы полностью понимаете, что они не могут быть реплицированы из ваш собственный код. Таким образом, лучше всего справляться с обеими сторонами процесса.

Это то, что я использую:

#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 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;
}