8

I have seen lengthy Objective-C answers to this here on stack overflow, but no swift answers.

How can I change the initial view controller programmatically in swift, from a view controller?

I would think it would go something like this:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
storyboard.setInitialViewController(identifier: ViewController())

But no, this doesn't do anything. The first line is good, but the second line's functions just don't exist.

owlswipe
  • 19,159
  • 9
  • 37
  • 82
  • Possible duplicate of [set initial viewcontroller in appdelegate - swift](http://stackoverflow.com/questions/26753925/set-initial-viewcontroller-in-appdelegate-swift) – Victor Sigler Mar 19 '16 at 19:37
  • Not a duplicate because the original question asks how it is done in app delegate but this asks how to set the initial view controller from a view controller. – Ankit Goel Mar 19 '16 at 20:03
  • 1
    Can you explain more exactly for what do you need to set the initial view controller from another view controller to help you better. – Victor Sigler Mar 19 '16 at 20:20
  • I added an answer to clarify you. – Victor Sigler Mar 19 '16 at 20:30

3 Answers3

12

To do it in the view controller and not in the app delegate: Just fetch the reference to the AppDelegate in your view controller and reset it's window object with the right view controller as it's rootviewController.

Step 1: Make some NSUserDefaults the user can adjust. A couple of buttons, some switches in a table view, something. Then when the user taps the button, we change the NSUserDefault.

@IBAction func SwitchLaunchViewtoViewController2(sender: AnyObject) {
  defaults.setObject("ViewController2", forKey: "LaunchView")
}
@IBAction func SwitchLaunchViewtoViewController1(sender: AnyObject) {
  defaults.setObject("ViewController1", forKey: "LaunchView")
}

Hook a couple of buttons in a settings view controller up to these functions, and we've started.

Step 2: Set up Storyboard IDs for all the storyboards you want to be able to be set as the launch view. So, for each View Controller that could be an initial view controller:

-Head into your storyboard.

-Click on the view controller.

-In the sidebar at right, click on the newspaper-like icon, which you control the class in.

-In the "Identity" section (third row), check "Use Storyboard ID" (make sure it's on) and then type in something like "VC1" in the "Storyboard ID" text field. Make sure you choose a different Storyboard ID for each view controller.

-Repeat for each view controller.

Step 3: Set up your initial view controller in the AppDelegate.swift file. Head into the func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool section of your app delegate.

Add this to read from the NSUserDefault you created earlier:

let defaults = NSUserDefaults.standardUserDefaults()
    if let launchview = defaults.stringForKey("LaunchView")
    {

}

This looks for an NSUserDefault string called "LaunchView" (which you created in step 1) and sets it to the new variable launchview if it finds a matching NSUserDefault.

Then, inside the if let launchview... brackets, we want to check what you set your LaunchView to. For every object you set to LaunchView in step 1 (in the example, I did "ViewController2" and "ViewController1"), you have to check for it here. So, inside those brackets, we add this:

if launchview == "ViewController2" {

} else if launchview == "ViewController1" {

}

Then, inside each of those if statements, we add the following code:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("YOUR_VC_IDENTIFIER") as! YourViewController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
appDelegate.window?.rootViewController = yourVC
appDelegate.window?.makeKeyAndVisible()

This will open the chosen window when your application finishes loading after it's been in the background a while.

Your finished didFinishLoadingWithOptions section of your AppDelegate might look something like this: (don't just copy and paste, read the instructions above)

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

let defaults = NSUserDefaults.standardUserDefaults()
        if let launchview = defaults.stringForKey("LaunchView")
        {

            if launchview == "ViewController1" {

        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("VC1") as! ViewController1
        appDelegate.window?.rootViewController = yourVC
        appDelegate.window?.makeKeyAndVisible()

            } else if launchview == "ViewController2" {
                let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
                appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
                let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("VC1") as! ViewController1
                appDelegate.window?.rootViewController = yourVC
                appDelegate.window?.makeKeyAndVisible()
            }

        }

        return true
   }

I hope this helps you, and many thanks to Ankit Goel who helped me with this one so much. Read the comments down below for more.

One final note: if you are using switches in a settings view, make sure on the viewDidLoad of that settings view controller you read from the NSUserDefault LaunchView which one the user selected last.

owlswipe
  • 19,159
  • 9
  • 37
  • 82
Ankit Goel
  • 6,257
  • 4
  • 36
  • 48
  • Thank you; where do I put all that code in my App Delegate (in what section?) and how do I "fetch the reference to the App Delegate in my view controller and reset its window object"? – owlswipe Mar 19 '16 at 19:57
  • Add this code in the view controller for e.g. you can put it in viewDidLoad() or viewDidAppear() or any other method.. – Ankit Goel Mar 19 '16 at 19:59
  • @AnkitGoel I'm afraid that your way it's not accurate at all, let suppose you have entered in the app with some initial view controller, when do you put your code exactly? You're setting the initial view controller after it has been passed, it's wrong. It's for that this code is used in the `application:didFinishLaunchingWithOptions:` – Victor Sigler Mar 19 '16 at 20:10
  • @VictorSigler The question specifies that he wants to do this from a View Controller!!! And put this code in viewDidLoad(), I have tested the code and it works.. This is particularly useful when you need to change the whole view controller hierarchy.. – Ankit Goel Mar 19 '16 at 20:13
  • @AnkitGoel Looks good, can you help me figure out how to find the View Controller identifier? What I mean is, how do I figure out what to put in the "YOUR_VC_IDENTIFIER"? – owlswipe Mar 20 '16 at 01:32
  • 1
    @JohnRamos Open your storyboard, click on the view controller, in the right pane click on third icon where you set the custome class for the view controller, just below that you will see the identity inspector where you can set your storyboardid for the viewcontroller – Ankit Goel Mar 20 '16 at 06:04
  • @AnkitGoel Can you tell me what this does. I successfully open my Calculator View when the user presses the "set calculator as initial view" switch. But my initial view controller is still another view. Does this not change my original view controller but instead I run this code on launch? – owlswipe Mar 21 '16 at 13:26
  • @AnkitGoel Oh I get this, I just link to the correct view controller in my AppDelegate, in the didFinishLaunchingWithOptions section! Then, it opens the right view based on an NSUserDefault value. – owlswipe Mar 21 '16 at 13:32
  • If this settles your question, please accept the answer. – Ankit Goel Mar 21 '16 at 13:33
  • @AnkitGoel I will edit your answer with everything you've taught me in the comments down here, and accept it! Thank you so much for all your help, I've done what I wanted!! – owlswipe Mar 21 '16 at 13:34
5

Updated for Swift 3

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

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

    window?.rootViewController = UINavigationController(rootViewController: ViewController())

    return true
}
iAj
  • 3,787
  • 1
  • 31
  • 34
2

First of all, setting the initial view controller programmatically of your app can be made in the application:didFinishLaunchingWithOptions: using the code exposed in the question:

You can manage all the conditions you want inside it to show one or another UIViewController in base of conditions.

For example let's say you want in your app show a walkthrough only the first time the app is installed, and after that walktrough another login screen and then another one, but you only show the walkthrough the first time and the login in case of not being logged before and the the another one.

This can be handled in several ways of course, I only try to explain you one of the ways of make it.

For this you can set a controller called SplashViewController for example, it's your initial UIViewController and inside it you show the image of the app (the big image logo, launchscreen) and your process when you go to one place or another. In this way you clean a lot the code inside your AppDelegate and it's more easily to make unit test for it.

Now if you want to go to another UIViewController from inside another UIViewController you can do it using the following code:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewControllerWithIdentifier("ControllerName") as! ControllerName
self.presentViewController(viewController, animated: true, completion: nil)

I hope this help you.

Community
  • 1
  • 1
Victor Sigler
  • 23,243
  • 14
  • 88
  • 105
  • You are assuming that the code needs to run when the app is launching(when it is mentioned in the question that this needs to be done from a view controller). If the user has already launched the app and he needs to reset the root view of the window then you either need to fire a local notification and observe it in app delegate or you can simply follow the solution given below. – Ankit Goel Mar 20 '16 at 06:24
  • @VictorSigler thank you very much for all your help, you certainly helped me find the solution to my problem (so I will give you a thumbs-up!). – owlswipe Mar 21 '16 at 14:02
  • @VictorSigler Also, if you gave a thumbs-down to Ankit Goel's answer, it would be really nice if you could remove the downvote because he really helped me too! You both were immeasurably helpful. – owlswipe Mar 21 '16 at 14:05
  • @JohnRamos Sure, with the answer provided now it deserves an upvote :) – Victor Sigler Mar 21 '16 at 14:45