frangulyan frangulyan - 1 year ago 87
iOS Question

iOS8 UISplitViewController's display mode button behavior

I have a problem in iOS8 when I have multiple detail views in

, the problem comes only in horizontally compact mode.

My storyboard looks similar to this:
enter image description here

As you can see, my master's table has two types of cells, each one is connected to "its own" hierarchy of detail views using Show Detail segue. So clicking first cell will load D1 (and its root) in secondary area of split view, clicking the second cell will load D2 there.

I am adding the split view's super smart display mode button to any detail's navigation item when preparing for segue:

controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;

Everything works perfect except one case:

  • I am in horizontally compact size (for example, iPhone 6 portrait)

  • Secondary area is shown, for example D1 screen. Primary is hidden.

  • On some occasion (like a button click in D1) I want to load D2 in the secondary area. I call
    to load it.

Now once I do that my magic display mode button shows "<D1" and clicking it goes back to D1! That is totally not the behavior that I want. This kind of 'history' back is desirable when I am navigating inside D1 or D2 itself (and I believe this button is smart enough to do that). But when I am on the top level, I want it to open the primary area. Basically I need more something like a left drawer opener (like in Android).

Rationale: My D1 is expressing some kind of error/incomplete state (no connection/ no user / etc) and when the state is 'fixed' then the 'normal' UI is loaded through D2 or D3 or any other. It is really strange to see a back button to an error page at this point. This history feature is even good when I go from D2 to D3, so if there is a way to somehow remove D1 from history - that will also help me.

In worst case I would do the button logic on my own - I would need a way to open the drawer (primary area), that info will also help me.

Answer Source

So, after struggling for a while, I found a way to get what I need. It comes out that in collapsed mode the split view "becomes" just a simple navigation controller under the hood. So there is a parent navigation controller on top of D2 (which is also a navigation controller). The code below written in viewDidLoad of the root controller of D2 does the trick:

UINavigationController* parentNavi = self.navigationController.navigationController;
if (parentNavi != nil) {
    // will get here only in collapsed mode
    NSMutableArray *controllers = [NSMutableArray arrayWithArray:parentNavi.viewControllers];
    NSMutableIndexSet *set = [NSMutableIndexSet indexSet];
    for (UIViewController *controller in controllers) {
        if ([controller.title isEqualToString:@"D1"]) {
            [set addIndex:[controllers indexOfObject:controller]];
    [controllers removeObjectsAtIndexes:set];
    parentNavi.viewControllers = controllers;

The code will enter the if statement only when you are in compact sizes. After cleaning up the controllers the display mode button points to the primary area. This code is also quite flexible - you can get rid of any page you don't need in stack.