10

I'm making an app where I have a key/value in NSUserDefaults which allows the app on start up to detect if this is the first time the app is turned on on the device. If it's the first time, I want the splitVC(which is also the rootVC)'s detail View to be my pageViewController(tutorial), else I want it to go straight into the app (another view controller, lets call it todayViewController).

I currently have a class for my SplitVC (GlobalSplitViewController.swift), but I currently have no idea how to programmatically change the detail view in ViewDidLoad.

Also, in storyboard, my splitVC's detail segue is connected to todayViewController and it's master segue to a menuVC, which is working perfectly.

Thanks in advance!

Code in GlobalSplitViewController.swift:

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

var firstTime: Bool!

override func viewDidLoad() {
    super.viewDidLoad()

    self.delegate = self
    firstTime = loadFistTime()

    if firstTime == true {
    //load tutorials pageVC
    } else {
   //load todayVC
    }

}

func splitViewController(svc: UISplitViewController, shouldHideViewController vc: UIViewController, inOrientation orientation: UIInterfaceOrientation) -> Bool {
    return true
}
comrade
  • 4,590
  • 5
  • 33
  • 48
Raymond Moay
  • 321
  • 1
  • 4
  • 16

5 Answers5

20

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)
Yaroslav
  • 2,435
  • 1
  • 19
  • 36
11

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
Altimir Antonov
  • 4,966
  • 4
  • 24
  • 26
  • Oh my... I did not think about `self.splitViewController?.viewControllers[1] = detailViewController`. Thank you!! – Raymond Moay Jun 30 '16 at 03:19
  • Please note this will crash if the split view controller fallback to navigation view controller (split screen with another app on iPad). – codingrhythm Jan 11 '21 at 23:17
4

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.

Gerd Castan
  • 6,275
  • 3
  • 44
  • 89
4

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

splitViewController.setViewController(viewController, for: .secondary)
pableiros
  • 14,932
  • 12
  • 99
  • 105
1

I have another proposal:

This code works fine on iPhone. The problem is with iPad (ONLY with iPad)

Navigation bar on detail view still disappears (And back button to, obviously) but ONLY on iPad.

On iPhone works perfectly.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

window = UIWindow(frame: UIScreen.main.bounds)

let firstVC = FIRST()
let secondVC = SECOND()

let firstNC = UINavigationController(rootViewController: firstVC)
let secondNC = UINavigationController(rootViewController: secondVC)

let splitViewController =  UISplitViewController()
splitViewController.viewControllers = [firstNC, secondNC]
splitViewController.preferredPrimaryColumnWidthFraction = 1/3
splitViewController.delegate = self

// IMPORTANT to show back button
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController

navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem

window?.rootViewController = splitViewController
window?.makeKeyAndVisible()

return true
}

// MARK: - Split view
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {

guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }

guard let topAsDetailController = secondaryAsNavController.topViewController as? SECOND 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
}

The question is:

What fails in the case of iPad? I repeat, this code on iPhone works PERFECTLY.

(And the scenario is: NO storyboard)

Markus
  • 1,147
  • 16
  • 26