7

I am using a UISplitViewController, with the master and the detail viewcontrollers, without UINavigationControllers.

In some cases (for example when clicking on a universal link), I would like to force the app to always show the master viewcontroller.

How can I do that?

Is there a way to switch back from detail to master programmatically?

Daniele B
  • 16,703
  • 21
  • 96
  • 154
  • Is this what you're looking for: http://stackoverflow.com/questions/25875618/uisplitviewcontroller-in-portrait-on-iphone-shows-detail-vc-instead-of-master – zombie Nov 22 '16 at 10:02
  • Hi @zombie the answer there seems to talk about always starting the app on master, by setting some delegate functions. But in my case, I would like to manually dismiss the detail view controller. There must be a way to do that? – Daniele B Nov 22 '16 at 10:10

4 Answers4

13

The split view controller is a beast, and the documentation is confusing. It is best understood by considering it as operating in two different modes: collapsed or not. Collapsed mode applies when the split view is presented in a horizontally compact view (i.e. iPhone), otherwise it is not collapsed (i.e. iPad).

The property preferredDisplayMode only applies if the view is NOT collapsed (i.e. iPad), and you can use this to select the master or detail view.

In collapsed mode, unless you are using navigation controllers, the original master view may be discarded:

After it has been collapsed, the split view controller reports having only one child view controller in its viewControllers property. The other view controller is collapsed into the other view controller’s content with the help of the delegate object or discarded temporarily

But it is much better to use navigation controllers, as the split view controller is designed to work in conjunction with them:

The split view controller knows how to adjust the interface in more intuitive ways. It even works with other container view controllers (like navigation controllers) to present view controllers.

If you are using navigation controllers then the original master view may be at the bottom of the navigation stack:

In a horizontally compact environment, the split view controller acts more like a navigation controller, displaying the primary view controller initially and pushing or popping the secondary view controller as needed

So you can do something like this:

if split.isCollapsed,
   let nav = split.viewControllers[0] as? UINavigationController
{
   nav.popToRootViewController(animated:false)
} else {
   split.preferredDisplayMode = .allVisible 
}

(It can get even more complicated if your master view pushes views in master as well as showing detail views. This code will pop to the root of the master view navigation stack)

Dale
  • 2,855
  • 21
  • 25
4

You can set the preferredDisplayMode

self.splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.allVisible

Or if you are looking for something like a toggle action:

extension UISplitViewController {
   func toggleMasterView() {
      let barButtonItem = self.displayModeButtonItem
      UIApplication.shared.sendAction(barButtonItem.action!, to: barButtonItem.target, from: nil, for: nil)
   }
}

Usage:

self.splitViewController?.toggleMasterView()
zombie
  • 4,242
  • 2
  • 19
  • 48
3

You can define a custom UISplitViewController and assign it to your split view in storyboard:

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
    }

    func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
        return true
    }
}
Saeed Ir
  • 1,021
  • 10
  • 15
1

My solution is to swap the position of your primary and secondary ViewControllers if user is using an iPad. Then set preferredDisplayMode = .primaryHidden. Example code below.

    splitViewVieController = UISplitViewController()
    let isIphone = UIDevice.current.userInterfaceIdiom == .phone
    splitViewVieController.viewControllers = isIphone ? [primaryNavController, seconaryNavController] : [seconaryNavController, primaryNavController]
    splitViewVieController.preferredDisplayMode = .primaryHidden

We can change the position or width of the primary ViewController if needed.

    splitViewVieController.maximumPrimaryColumnWidth = splitViewVieController.view.bounds.width
    splitViewVieController.preferredPrimaryColumnWidthFraction = 0.5
    splitViewVieController.primaryEdge = .trailing
Jeffrey Chang
  • 352
  • 1
  • 4
  • 18