简体   繁体   中英

UISplitViewController's detail view push error

I'm implementing a universal app using UISplitViewController for iOS8 and facing strange problem with the UINavigation and would really appreciate your expertise.

My project has the following StoryBoard layout: UISplitViewController中的UINavigation

On iPad, everything is working as expected. However, running on the iPhone, the navigation doesn't work as expected. Please see this short video demonstrating the navigation problem as I navigate from "Detail Screen 2" back to "Detail Screen 1".

I tried implementing this same scenario on a brand new project but I did not see the problem. Only after porting into my existing project do I see this behavior.

UPDATE 1 :

Here is my AppDelegate code:

    @interface AppDelegate () <UISplitViewControllerDelegate>

    @end

    @implementation AppDelegate

    -(BOOL) application: (UIApplication*) application didFinishLaunchingWithOptions: (NSDictionary*) launchOptions {

        UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
        UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
        navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
        splitViewController.delegate = self;

        splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;

        return YES;
    }

    #pragma mark - Split view

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

        return YES;
    }

....
@end

UPDATE 2:

Thanks to PetahChristian, I think his observation below is correct:

To collapse a secondary view controller which has a navigation controller, Apple inserts the secondary navigation controller onto the primary navigation controller's stack. So, for the iPhone, where you see the problems, it looks like there is only one navigation controller, but there actually are two.

Assuming that observation is correct, how can the secondary navigation controller be prevented from pushing onto the primary navigation controller? The UISplitViewControllerDelegate methods only handle collapse logic for secondary view controller DIRECTLY linked to the UISplitViewController . In my case, the secondary view controller to be collapsed (namely Detail VC1 ) is routed via " Show Detail (eg Replace) " segue from the master view controller and the UISplitViewControllerDelegate methods doesn't execute during this transition.

With the exact same setup on a brand new project, Apple doesn't insert the secondary navigation controller onto the primary navigation controller and I don't experience this problem on a new project.

Many thanks.

The culprit causing this navigation peculiarity in my project was due to an extension I downloaded from SO user called UIViewController+BackButtonHandler . This handler intercept the navigation back button so I can get the chances to do extra work when user press back. This category extension code overrides navigationBar:shouldPopItem: causing the default navigation to break. I had no idea this code was executing because I wasn't utilizing it but rather, just incorporating in my project. Wow... 2 days of banging my head against the wall.

It behaves differently between projects because the UISplitViewControllerDelegate code is different.

The new project has the necessary code, but the existing project may be missing it.

Check your AppDelegate and compare the code that handles collapsing and separating the secondary view controller.

-splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
-splitViewController:separateSecondaryViewControllerFromPrimaryViewController:

Update:

You shouldn't unconditionally return YES in splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: . You need to first determine which view controller is on the top: See how they check to see if it is the (secondary navigation controller, and its child) detail view controller, and it has details to show?

- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
    if ([secondaryViewController isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]] && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {
        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;
    } else {
        return NO;
    }
}

Update 2:

To collapse a secondary view controller which has a navigation controller, Apple inserts the secondary navigation controller onto the primary navigation controller's stack. So, for the iPhone, where you see the problems, it looks like there is only one navigation controller, but there actually are two.

In your video, when it looks like you are at the master, but it has a back button, and you tap it, and the back button slides off the screen, that's the secondary navigation controller disappearing from the primary navigation controller.

For your project to properly work, you'll have to conditionally test for the navigation controller, like apple does, to determine whether the (navigation controller and its child) detail view controller get discarded or not.

Update 3:

A navigation controller, in itself, is a view controller. It controls its children view controllers.

The secondary navigation controller is the secondary view controller. The detail view controller is its child view controller.

What the splitView delegate methods are handling are the collapse or separation of the secondary navigation controller, which happens to have one or more child view controllers.

When the splitView controller is collapsed, the primary navigation controller's stack looks like [masterViewController, secondaryNavigationController].

As for the replace segue, what you are replacing is the Empty detail view controller in the bottom of the Storyboard, with the orange Detail Screen 1. But the replaced detail view controller still has a parent, the secondary navigation controller. And when the splitViewController collapses, the secondary navigation controller ends up on the primary navigation controller stack. You don't see this, because it's transparent. All you see is the Detail screen 1 because it's the top view controller of the secondary navigation controller.

Your splitViewDelegate code is broken. This is why the view controllers don't animate properly in the video when you tap the Back button. Fix the delegate code, and all will look and work correctly.

You definitely need both the collapse and the separate methods, and they need to do the right thing. That's why I'm recommending you use Apple's code, instead of trying to write your own, because Apple's code correctly collapses and separates the navigation controller.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM