Setting Initial View Controller Programmatically Swift

22,065

Solution 1

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.

Solution 2

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
}

Solution 3

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.

Share:
22,065
owlswipe
Author by

owlswipe

“Gratitude is the ability to experience life as a gift. It liberates us from the prison of self-preoccupation.” –John Ortberg “If you love a flower, don’t pick it up. Because if you pick it up it dies and it ceases to be what you love. So if you love a flower, let it be. Love is not about possession. Love is about appreciation.” ―Osho "Never be ashamed of what you feel. You have the right to feel any emotion that you want, and to do what makes you happy. That's my life motto." –Demi Lovato "A dog is the only thing on earth that loves you more than you love yourself." –Josh Billings Icon credit the Micopi Pico app.

Updated on July 12, 2022

Comments

  • owlswipe
    owlswipe almost 2 years

    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
    owlswipe about 8 years
    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"?
  • Ankit Goel
    Ankit Goel about 8 years
    Add this code in the view controller for e.g. you can put it in viewDidLoad() or viewDidAppear() or any other method..
  • Victor Sigler
    Victor Sigler about 8 years
    @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:
  • Ankit Goel
    Ankit Goel about 8 years
    @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..
  • owlswipe
    owlswipe about 8 years
    @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"?
  • Ankit Goel
    Ankit Goel about 8 years
    @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
    Ankit Goel about 8 years
    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.
  • owlswipe
    owlswipe about 8 years
    @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
    owlswipe about 8 years
    @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.
  • Ankit Goel
    Ankit Goel about 8 years
    If this settles your question, please accept the answer.
  • owlswipe
    owlswipe about 8 years
    @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
    owlswipe about 8 years
    @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
    owlswipe about 8 years
    @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.
  • Victor Sigler
    Victor Sigler about 8 years
    @JohnRamos Sure, with the answer provided now it deserves an upvote :)