Instantiate and Present a viewController in Swift
Asked Answered
H

18

352

Issue

I started taking a look on the Swift Programming Language, and somehow I am not able to correctly type the initialization of a UIViewController from a specific UIStoryboard.

In Objective-C I simply write:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"StoryboardName" bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"ViewControllerID"];
[self presentViewController:viewController animated:YES completion:nil];

Can anyone help me on how to achieve this on Swift?

Hernadez answered 4/6, 2014 at 11:16 Comment(0)
C
744

This answer was last revised for Swift 5.4 and iOS 14.5 SDK.


It's all a matter of new syntax and slightly revised APIs. The underlying functionality of UIKit hasn't changed. This is true for a vast majority of iOS SDK frameworks.

let storyboard = UIStoryboard(name: "myStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "myVCID")
self.present(vc, animated: true)

Make sure to set myVCID inside the storyboard, under "Storyboard ID."

Conscientious answered 4/6, 2014 at 11:22 Comment(18)
You can even omit the ;! ;) Would you mind elaborating on the as UIViewController? Why is that necessary?Brownnose
as keyword is used for typecasting. It's the same as (UIViewController *)anObject in objective cWolverine
Yeah I know I can omit them but that's a part of the long habbit. :D As instantiateViewControllerWithIdentifier returns AnyObject (id equivalent in Swift) and I declare vc as UIViewController, I have to typecast AnyObject to UIViewController.Conscientious
Hi Friends, i am facing some different issue, This code run on simulator not on my ipad2 which having iOS v7.1.2. If you dont mind help me to resolve this issue.Richman
the "as UIViewController" can also be "as CustomViewControllerSubclass". You're not always going to want to instantiate just a UIViewController from a storyboard.Ila
@Conscientious Most definitely, to you. But maybe not to some.Ila
with Swift 1.2, the as should be changed to as! (more info: developer.apple.com/swift/blog/?id=23)Noletta
As of Swift 2 instantiateViewControllerWithIdentifier returns UIViewController so you don't need to unroll anymore. So things simplify: let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil); let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController"); self.presentViewController(vc, animated: true, completion: nil)Iritis
for ios9 as! UIViewController no need it "Forced cast of 'UIViewController' to same type has no effect"Larson
If I go to second viewController in tabBarController, How do I do? If I direct to second viewController than does not show TavBarController.Fateful
Is it necessary to instantiate the storyboard object each time or I thing it can be made static to reuse it. Am I correct with this approach.Armington
@Conscientious In the new syntax. I don't need the extra "as UINavigationController" then? In let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")Doubleminded
I don't think completion is required.Keifer
it's better to use with navigation self.navigationController?.show(controller, sender: nil)Celluloid
@Celluloid It's not.Conscientious
yes, it is. if you have more than one controller then you have to add navigation controllerCelluloid
it showing me condition of iOS 13 ?? how can I do for iOS 12 ? Thank You.Lockage
Thanks @Conscientious for the answer. Just wanted to add one thing if anyone wants to push the view controller from the navigation controller then the one could replace the last line i.e. self.present(vc, animated: true) by this line self.navigationController?.pushViewController(vc, animated: true)Thurible
H
45

For people using @akashivskyy's answer to instantiate UIViewController and are having the exception:

fatal error: use of unimplemented initializer 'init(coder:)' for class

Quick tip:

Manually implement required init?(coder aDecoder: NSCoder) at your destination UIViewController that you are trying to instantiate

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

If you need more description please refer to my answer here

Hernadez answered 4/6, 2014 at 11:30 Comment(5)
If you want to use custom initiator you can just call the super with super.init(nibName: nil, bundle: nil) and the whew will load fine or do call it with the name of them NIB file.Kenzie
@user1700737 I am instantiating the viewController referring to storyboard, which executes initWithCoder, not initWithNib. Refer to this question #24036893Hernadez
Hi Friends, i am facing some different issue, This code run on simulator not on my ipad2 which having iOS v7.1.2. If you dont mind help me to resolve this issue.Richman
@Richman you need to ask another question on stack, give me the link Ill be happy to help you!Hernadez
On Swift 1.2 you must make init(coder aDecoder: NSCoder!) "required". So now you have to write the following: required init(coder aDecoder: NSCoder!)Ultrafilter
D
20

This link has both the implementations:

Swift:

let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as UIViewController
self.presentViewController(viewController, animated: false, completion: nil)

Objective C

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

This link has code for initiating viewcontroller in the same storyboard

/*
 Helper to Switch the View based on StoryBoard
 @param StoryBoard ID  as String
*/
func switchToViewController(identifier: String) {
    let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(identifier) as! UIViewController
    self.navigationController?.setViewControllers([viewController], animated: false)

}
Deferred answered 21/8, 2015 at 11:26 Comment(0)
S
15

akashivskyy's answer works just fine! But, in case you have some trouble returning from the presented view controller, this alternative can be helpful. It worked for me!

Swift:

let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
// Alternative way to present the new view controller
self.navigationController?.showViewController(vc, sender: nil)

Obj-C:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MyStoryboardName" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"someViewController"];
[self.navigationController showViewController:vc sender:nil];
Shanitashank answered 6/11, 2015 at 15:11 Comment(0)
U
14

Swift 4.2 updated code is

let storyboard = UIStoryboard(name: "StoryboardNameHere", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ViewControllerNameHere")
self.present(controller, animated: true, completion: nil)
Unweighed answered 5/12, 2018 at 13:14 Comment(0)
C
7
// "Main" is name of .storybord file "
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
// "MiniGameView" is the ID given to the ViewController in the interfacebuilder
// MiniGameViewController is the CLASS name of the ViewController.swift file acosiated to the ViewController
var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("MiniGameView") as MiniGameViewController
var rootViewController = self.window!.rootViewController
rootViewController?.presentViewController(setViewController, animated: false, completion: nil)

This worked fine for me when i put it in AppDelegate

Chokedamp answered 8/1, 2015 at 17:17 Comment(0)
B
7

If you want to present it modally, you should have something like bellow:

let vc = self.storyboard!.instantiateViewControllerWithIdentifier("YourViewControllerID")
self.showDetailViewController(vc as! YourViewControllerClassName, sender: self)
Bonacci answered 26/5, 2016 at 20:26 Comment(0)
B
7

I would like to suggest a much cleaner way. This will be useful when we have multiple storyboards

1.Create a structure with all your storyboards

struct Storyboard {
      static let main = "Main"
      static let login = "login"
      static let profile = "profile" 
      static let home = "home"
    }

2. Create a UIStoryboard extension like this

extension UIStoryboard {
  @nonobjc class var main: UIStoryboard {
    return UIStoryboard(name: Storyboard.main, bundle: nil)
  }
  @nonobjc class var journey: UIStoryboard {
    return UIStoryboard(name: Storyboard.login, bundle: nil)
  }
  @nonobjc class var quiz: UIStoryboard {
    return UIStoryboard(name: Storyboard.profile, bundle: nil)
  }
  @nonobjc class var home: UIStoryboard {
    return UIStoryboard(name: Storyboard.home, bundle: nil)
  }
}

Give the storyboard identifier as the class name, and use the below code to instantiate

let loginVc = UIStoryboard.login.instantiateViewController(withIdentifier: "\(LoginViewController.self)") as! LoginViewController
Bellyful answered 13/8, 2018 at 11:14 Comment(2)
Whats @nonobjc for ?Gers
@Gers this will not be available to objective cAntherozoid
S
5

No matter what I tried, it just wouldn't work for me - no errors, but no new view controller on my screen either. Don't know why, but wrapping it in timeout function finally made it work:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let controller = storyboard.instantiateViewController(withIdentifier: "TabletViewController")
    self.present(controller, animated: true, completion: nil)
}
Score answered 8/3, 2018 at 11:58 Comment(1)
Interesting. No async dispatch is needed usually unless the code is executed on a background threadArtiodactyl
U
3

Swift 3 Storyboard

let settingStoryboard : UIStoryboard = UIStoryboard(name: "SettingViewController", bundle: nil)
let settingVC = settingStoryboard.instantiateViewController(withIdentifier: "SettingViewController") as! SettingViewController
self.present(settingVC, animated: true, completion: {

})
Unsophisticated answered 13/5, 2017 at 3:10 Comment(0)
C
3

Swift 4:

    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let yourVC: YourVC = storyboard.instantiateViewController(withIdentifier: "YourVC") as! YourVC
Cenacle answered 18/5, 2018 at 19:36 Comment(0)
I
2

If you have a Viewcontroller not using any storyboard/Xib, you can push to this particular VC like below call :

 let vcInstance : UIViewController   = yourViewController()
 self.present(vcInstance, animated: true, completion: nil)
Induce answered 14/3, 2017 at 20:19 Comment(2)
I get this: "cannot be constructed because it has no accessible initializers".Continuator
the outlets and other relationships made by the storyboard are lostNoddle
A
2

Swift 5

let vc = self.storyboard!.instantiateViewController(withIdentifier: "CVIdentifier")
self.present(vc, animated: true, completion: nil)
Arctic answered 6/1, 2020 at 13:7 Comment(0)
S
1

I know it's an old thread, but I think the current solution (using hardcoded string identifier for given view controller) is very prone to errors.

I've created a build time script (which you can access here), which will create a compiler safe way for accessing and instantiating view controllers from all storyboard within the given project.

For example, view controller named vc1 in Main.storyboard will be instantiated like so:

let vc: UIViewController = R.storyboard.Main.vc1^  // where the '^' character initialize the controller
Simpleton answered 25/2, 2017 at 19:17 Comment(0)
O
0

I created a library that will handle this much more easier with better syntax:

https://github.com/Jasperav/Storyboardable

Just change Storyboard.swift and let the ViewControllers conform to Storyboardable.

Onward answered 13/10, 2018 at 12:59 Comment(0)
W
0
guard let vc = storyboard?.instantiateViewController(withIdentifier: "add") else { return }
        vc.modalPresentationStyle = .fullScreen
        present(vc, animated: true, completion: nil)
Whoreson answered 12/11, 2020 at 12:30 Comment(1)
Please add some explanation to your answer. Explaining the underlying logic is more important than just giving the code. It helps the author and other readers fix this and similar issues themselves while giving them the clues to the knowledge they need to expand their programming skills.Take
E
0
if let destinationVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DestinationVC") as? DestinationVC{
            let nav = self.navigationController
            //presenting
            nav?.present(destinationVC, animated: true, completion: {
                
            })
            //push
            nav?.pushViewController(destinationVC, animated: true)
        }
Ephialtes answered 17/8, 2022 at 4:17 Comment(1)
Your answer could be improved with additional supporting information. Please add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help centerSaunder
G
0

I use this helper:

struct Storyboard<T: UIViewController> {
    
    static var storyboardName: String {
        return String(describing: T.self)
    }
    
    static var viewController: T {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        
        guard let vc = storyboard.instantiateViewController(withIdentifier: Self.storyboardName) as? T else {
            fatalError("Could not get controller from Storyboard: \(Self.storyboardName)")
        }
        
        return vc
    }
}

Usage (Storyboard ID must match the UIViewController class name)

let myVC = Storyboard.viewController as MyViewController
Gnomic answered 24/11, 2022 at 6:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.