Open UISplitViewController to Master View rather than Detail

I have a split-view interface with a target iPhone 6 application. On the first launch of the application, it opens to the Detail View; I would like it to open to the Master View. I have tried:

self.splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryOverlay

Which was suggested elsewhere (Prior StackOverFlow Question) but it doesn't seem to do anything, and does not open the Master view on launch. I also tried to add the following line to my AppDelegate:

splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:

But despite returning true or false (Another Prior Stack Overflow Question) I had no success.

I did launch up the example Master-Detail application in Xcode, and it loads to the Master view based on the splitViewController: call returning false; however, I'm not sure how to make this work in a more complicated layout.


Solution 1:

Swift

UISplitViewController display master view above detail in portrait orientation is not about showing the Master view, it is about presenting the Detail view in full width, underneath the Master view.

UISplitViewController in portrait on iPhone shows detail VC instead of master is about the principle of the collapse mechanism.

This present answer addresses:

  • Master → Detail (Compact width)
    • iPhone 4s, 5, 5s, SE, 6, 6s, 7 (any orientation)
    • iPod Touch
    • any iPhone Plus (portrait)
  • side-by-side (all other sizes)
    • iPad
    • any iPhone Plus (landscape)

You must set preferredDisplayMode. You would want is .primaryVisible if it existed! Using .allVisible, iOS picks Detail if only 1 view fits (Compact width); in that size, the code below will pick Master.

The trick is to change both the preferredDisplayMode to .allVisible and to return true in collapseSecondary:onto.

class PrimarySplitViewController: UISplitViewController,
                                  UISplitViewControllerDelegate {

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

    func splitViewController(
             _ splitViewController: UISplitViewController,
             collapseSecondary secondaryViewController: UIViewController,
             onto primaryViewController: UIViewController) -> Bool {
        // Return true to prevent UIKit from applying its default behavior
        return true 
    }
}

Solution 2:

iOS 14

I wasn't getting a callback for splitViewController(_:collapseSecondary:onto:) and instead used the following new method.

func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
    return .primary
}

Solution 3:

Step 1 - Open MasterViewController

Step 2 - ensure the table view has the UISplitViewControllerDelegate protocol. Eg:

class ListVC: UITableViewController,UISplitViewControllerDelegate {}

Step 3 - Add it in ViewDidLoad

splitViewController?.delegate = self

Step 4 - Then override this method to say the master view controller should always collapse onto the detail view controller:

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
    return true
}

Solution 4:

On the first launch of the application, it opens to the Detail View; I would like it to open to the Master View

Assuming you want that only on the first launch, but not always; for example in the case that the Master View shows an empty data set; then the solution is just as the Master-Detail template shows:

func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool {
    guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
    guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
    if topAsDetailController.detailItem == nil {
        // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return true
    }
    return false
}