Instantiate a view controller using a storyboard identifier in Xcode (update Swift 3: Xcode 8, beta 6)


** Jump to Swift 3 code **

In order to instantiate a view controller using a storyboard identifier in Xcode, you first need to assign a Storyboard ID to your view controller. In order to do this:

(1) select the storyboard file in your project

(2) select the view controller that you would like to instantiate

(3) with the view controller highlighted select the identity inspector in the utilities on the right-hand side of the window

(4) give your view controller a Storyboard ID


(5) now wherever you need to instantiate the view controller you simply write the following code (making sure that the strings you pass to storyboardWithName: and instantiateViewControllerWithIdentifier: match the name of the Storyboard and the Storyboard ID exactly, because Xcode won't catch any errors in these, it will instead crash at runtime)

UIViewController *viewController = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:@"ViewController"];

(6) be aware that you must not place this code inside viewDidLoad. It is only within viewDidAppear: and methods later in the lifecycle where the presentation can take place and often a presentation will take place due to a user interaction.

Update 2nd March 2016: And in Swift, it would look like this

let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController")

self.presentViewController(viewController, animated: false, completion: nil)

If we wish to call methods specific to a UIViewController subclass, however, we need to do two things:

(1) ensure that in the Class field we select the class we wish to utilise (see the box above the Storyboard ID in the above image)

(2) that we typecast to the subclass rather than accept the compiler's assumption that we are instantiating a regular UIViewController.

The code for this might look something like this:
@IBAction func buttonPressed(sender:AnyObject) {
        if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as? ViewController {
            self.presentViewController(viewController, animated: false, completion: nil)
            viewController.sayHello()
            
        }
    }
    func sayHello() {
        print("Hello")
    }
For this to work we need to wire up the IBAction to a button on our root view controller. Also note that the root view controller and presented view controller must share a class type for this to work or the sayHello method must be shifted to the presented view controller's class declaration.

Note: if the presented view controller and the presenting view controller are the same class then do not place presentation code in viewDidAppear: because this would mean view controllers were created infinitely.

It is also worth noting here that not only can the presenter call methods from the presented view controller's class but that the presented view controller can utilise methods in the presenting view controller:
@IBAction func buttonPressed(sender:AnyObject) {
        if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as? ViewController {
            self.presentViewController(viewController, animated: false, completion: nil)
            viewController.sayHello()
            if let presenter = viewController.presentingViewController as? ViewController { presenter.sayGoodbye() }
            
        }
    }
    func sayHello() {
        print("Hello!")
    }
    
    func sayGoodbye() {
        print("Goodbye!")
    }
Once again, here the presenting view controller and the presented view controller share a common class.

Swift 3 (Xcode 8 beta 6)

In Swift 3 this becomes:
@IBAction func buttonPressed(sender:AnyObject) {
    if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as? ViewController {
        self.present(viewController, animated: false, completion: nil)
        viewController.sayHello()
        if let presenter = viewController.presentingViewController as? ViewController { presenter.sayGoodbye() }
            
    }
}
func sayHello() {
    print("Hello!")
}
    
func sayGoodbye() {
    print("Goodbye!")
}
And the simple instantiation is changed to:
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController")
self.present(viewController, animated: false, completion: nil)


Comments

  1. hey is this still up to date for objc? plus i'm fairly new to this, so where would i put the code?
    basically i want to make a storyboard viewcontroller for an apple tv but not sure how to do that
    thnx

    ReplyDelete
    Replies
    1. You'll want to look at AirPlay if working with AppleTV - https://developer.apple.com/airplay/ - and also working with second screens - https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/WindowAndScreenGuide/UsingExternalDisplay/UsingExternalDisplay.html

      Delete
  2. Thank you. Most of examples of this code (display modal view) has old syntax. This is first page I searched having right swift syntax. Thanks.

    ReplyDelete
  3. Thanks for the code i only got this warning:

    Warning: Attempt to present on whose view is not in the window hierarchy!

    ReplyDelete
    Replies
    1. This will happen if you place the code inside viewDidLoad. Try placing the code within viewDidAppear: or inside the action for a button push.

      Delete
  4. Replies
    1. See response above about not placing the code inside viewDidLoad.

      Delete
  5. Wonderful post! We will be linking to this particularly great article on our website.Keep up the great writing.
    storyboard animation

    ReplyDelete
  6. Thanks. This is working. But what I need is a way to pass information to the new instance. Can you recommend something?

    ReplyDelete

Post a Comment