12

Since iOS8 we're allowed to use UISplitViewController on both compact and regular devices. This is great because I don't have to create two different storyboard for iPhone and iPad, but there's one problem that I'm stuck with.

If the split view controller is on iPad(if the collapsed property is NO), I can simply call this to show MasterVC on the left side.

self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;
[self.splitViewController.displayModeButtonItem action];

But if it's on iPhone(if the collapsed property is YES), the displayMode is ignored, and doesn't do anything.

I cannot pop DetailVC with popToRootViewControllerAnimated because DetailVC has it's own navigation controller.

How does Apple expect us to show MasterVC(dismiss DetailVC) in code in collapsed mode if there isn't any method like dismissViewControllerAnimated:completion: for view controller that was presented with showDetail? Your help will be appreciated. Thanks

SFF
  • 877
  • 2
  • 11
  • 26

4 Answers4

24

On devices which don't support the "split" mode, if

  1. You want to present the master view controller instead of the detail when the UISplitViewController first loads, then returning YES in your delegate class (UISplitViewControllerDelegate) splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: method method should do that:

    - (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
        return YES;
    }
    
  2. You want to dismiss the detail view controller back to the master, after a specific event (e.g. a touch on a button). In this case you have to pop the detail view controller navigation controller:

    [detailViewController.navigationController.navigationController popToRootViewControllerAnimated:YES]
    
pNre
  • 5,376
  • 2
  • 22
  • 27
  • Thanks for your reply! I totally forgot about the delegate methods. But mine is already returning YES. I'm trying to dismiss the DetailVC/show the MasterVC on a certain event. I'm looking for something like `dismissViewControllerAnimated:completion:` for DetailVC. But I'll check more into delegate if there's anything. – SFF May 08 '15 at 11:18
  • 3
    @SFF Have you tried popping the detail navigation controller parent navigation controller? Something like `[detailViewController.navigationController.navigationController popToRootViewControllerAnimated]`. – pNre May 08 '15 at 12:20
  • I had no idea I could do that! That worked for me. Would you mind adding it to the answer?? Thank you so much. – SFF May 08 '15 at 20:17
  • Does anyone receive this warning log when this is executed? `Unbalanced calls to begin/end appearance transitions for ` – Jack Aug 15 '16 at 18:49
6

Had a similar issue today trying to pop back from a detail view in a split view controller.

While I'm sure the accepted answer works fine, another approach I found that works as well and may be a bit cleaner is to use an unwind segue.

I setup an unwind segue on the master view I wanted to return to, then created a segue link to the unwind segue from the view I wanted to pop (note: assumes that you are using storyboards).

Make sure to setup the IBAction on the destination view you are popping back to:

-(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue { }

Connect the exit to the segue in the storyboard for the unwind segue. Sorry, I'm not providing a lot of detail on how to setup the unwind segue, but there are many tutorials available for that.

Then on your controller you want to dismiss, connect a segue to the unwind segue of the controller you are popping back to. Be sure to name the segue.

Then on the button touch in the view controller you want to dismiss, just call

[self performSegueWithIdentifier:@"unwindSegueName" sender:self];

This worked really well and avoids digging backwards into a navigation hierarchy that may change.

Hope this is useful to someone! Happy Holidays!

CocoaEv
  • 2,984
  • 20
  • 21
  • 1
    Thank you for this. One detail that I got from [an answer to a similar question](http://stackoverflow.com/a/27878155) is that it only makes sense to do an unwind segue if the `UISplitViewController` is currently collapsed (and indeed attempting it on an uncollapsed controller causes a crash) so putting the call inside an `if (self.splitViewController.isCollapsed)` block makes it robust for all configurations. – Jon Colverson Nov 23 '16 at 19:08
  • The point of adaptive segues is they work in both cases with no need to check for collapsed. – malhal Jul 23 '18 at 13:02
2

Here's what I ended up doing to pop the DetailVC if we are in a collapsed state (iPhone excluding +sizes), and show/hide the MasterVC if we are not in a collapsed state (iPad).

@IBAction func backTouchUp(_ sender: UIButton) {
    if let splitViewController = splitViewController,
        !splitViewController.isCollapsed {
        UIApplication.shared.sendAction(splitViewController.displayModeButtonItem.action!, to: splitViewController.displayModeButtonItem.target, from: nil, for: nil)
    } else {
        navigationController?.popViewController(animated: true)
    }
}
Cody
  • 650
  • 9
  • 16
0

Thanks pNre! Here's code that will handle displaying a custom back button when collapsed and the displayModeButton when not collapsed.

lazy var backButtonItem: UIBarButtonItem = {
    UIBarButtonItem(image: UIImage(named: "backImage"), style: .plain, target: self, action: #selector(dismissAnimated))
}()

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    guard let svc = splitViewController else { return }

    if svc.isCollapsed {
        navigationItem.leftBarButtonItem = backButtonItem
    } else {
        navigationItem.leftBarButtonItem = svc.displayModeButtonItem
    }
}

func dismissAnimated() {
    _ = navigationController?.navigationController?.popViewController(animated: true)
}

I've placed this in willLayoutSubviews() instead of viewDidLoad() so that the button will be updated adaptively, e.g., for orientation changes on iPhone 7 Plus and size class changes such as while in split view on iPad.

Scott Gardner
  • 8,603
  • 1
  • 44
  • 36