How to change detail View in splitViewController programatically in Swift

calling

splitViewController.showDetailViewController(vc, sender: self)

appears to be a good solution. But when called on an iPhone when a detailViewController is shown already on top of the masterViewController, then this call presents the detailViewController without navigationViewController and without back button. So the app is stuck.

Popping the old detailViewController and showing the new one also shows the same problem described above.

What works is popping the old one, wait until the work is finished and then showing the new one.

The following code assumes to be implemented in the masterViewController:

if let top = navigationController?.topViewController,
   top !== self {
    if let navController = splitViewController.viewControllers[0] as? UINavigationController {
        navController.popViewController(animated: false)
        DispatchQueue.main.async {
            splitViewController.showDetailViewController(vc, sender: self)
        }
        return
    }
}
splitViewController.showDetailViewController(vc, sender: self)

Th critical part is DispatchQueue.main.async. This assures that popping the old detail is finished before showing the new detail.


If you are using side bar style implementation and iOS 14+:

splitViewController.setViewController(viewController, for: .secondary)

In AppDelegate for example you can check your UserDefaults, and with Switch or If/else you can change the splitView. Here is an example of changing the detailViewController.

let detailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("DetailNavigationViewController") as! UINavigationController
self.splitViewController?.viewControllers[1] = detailViewController

Starting with iOS 8 you can use:

splitViewController?.showDetailViewController(vc, sender: self)

or if you want to replace primary controller

splitViewController?.show(vc, sender: self)