50

I am currently furiously digging through all the docs, and haven't quite found what I'm looking for. I suspect it is a real d'oh! answer.

I simply need to find the active storyboard in the main bundle, and want to know the best way to do this.

This is so that I can use the [UIStoryboard storyboardWithName:@"XXX" bundle:mainBundle] to extract the running storyboard.

I know how to kludge it by switching on the idiom, but I feel that this is a...kludge.

What's a correct way of doing this?

pkamb
  • 33,281
  • 23
  • 160
  • 191
Chris Marshall
  • 4,910
  • 8
  • 47
  • 72

5 Answers5

41

In case you want to get the active storyboard for a viewController, there's a storyboard property. This is how I solved it, instead of making a new instance:

LoginViewController *vc = [navController.storyboard instantiateViewControllerWithIdentifier:@"firstLaunch"];
[navController presentModalViewController:vc animated:YES];

In Swift you'd call:

let loginViewController = navigationController?.storyboard?.instantiateViewController(withIdentifier: "firstLaunch") as! LoginViewController
navigationController?.present(loginViewController, animated: true, completion: nil)

You could also be a lot safer by using guards against the navigation controller and the storyboard. I've used as! so as to guarantee that you're getting a LoginController.

pkamb
  • 33,281
  • 23
  • 160
  • 191
olivaresF
  • 1,369
  • 2
  • 14
  • 28
  • +1 for the .storyboard property. This property is not only available for a navigation controller but also for UITableViewController, UITabBarController, UICollectionViewController, etc. – Leon Dec 22 '15 at 11:36
27

OK. As my comment above indicates, I found the answer to the (badly phrased question):

I wanted to be able to get the main (not active) storyboard, as I'm not using multiple storyboards per incarnation. I'm using the standard model of 1 storyboard for iPhone, and 1 for iPad. I just wanted the cleanest way to get the storyboard, so that I could use it to generate a view controller.

I found the answer in this post on Stack Overflow, and implemented it with the following code:

UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:@"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
Community
  • 1
  • 1
Chris Marshall
  • 4,910
  • 8
  • 47
  • 72
  • 12
    calling `[UIStoryboard storyboardWithName:...` creates a new instance of the storyboard. This won't give you access to the same instances that are actually loaded. – Matt Connolly Apr 06 '12 at 01:49
  • 2
    Thanks. Yeah, but that's OK. I want a new instance. The already loaded one is off doing something else. – Chris Marshall Apr 06 '12 at 11:42
  • Definitely the best answer for my problem. Thanks! – mpemburn Feb 23 '13 at 16:57
  • 1
    Or may be adding this to the AppDelegate as a Class method, so you can reuse the storyboard and avoid creating unnecessary instances. – Frederic Yesid Peña Sánchez Oct 18 '13 at 17:11
  • Yeah...not a big deal to do that. But why bother, if it is only gonna be used once? Was that REALLY worth a downvote? – Chris Marshall Oct 06 '14 at 15:08
  • @FredericYesidPeñaSánchez Good point, I've done this to avoid creating additional instances. Create storyboard once in ``application:DidFinishLaunching... `` or hold onto a ``vc.storyboard`` that was already created by system – Protongun Dec 14 '16 at 16:13
7

In Swift, you'd use the following syntax:

let storyboard = UIStoryboard(name: "Main", bundle: nil) 

Note that passing nil to bundle will make the call refer to your main bundle automatically.

If you're in a view controller that you have on the Storyboard and want to instantiate the Storyboard from there directly, you can just do:

let storyboard: UIStoryboard? = self.storyboard // call this inside a VC that is on the Storyboard

Note that in the last case, self.storyboard will return an optional Storyboard (Storyboard?), so if you'd like to use it unwrap it like so:

if let storyboard = self.storyboard {
  // access storyboard here
}
nburk
  • 22,409
  • 18
  • 87
  • 132
1

The better approach would be to get existing storyboard instance from window's rootViewController if not available then create a new instance.

let storyboard = self.window?.rootViewController?.storyboard ?? UIStoryboard.init(name: "Main", bundle: nil) 

Also using globally accessible helper functions like these can be a good idea provided that you pass the already active storyboard as parameter.

class Helper {
    static func getLoginVC(storyboard: UIStoryboard) -> LoginVC {
         return storyboard.instantiateViewController(withIdentifier: String(describing: LoginVC.self)) as! LoginVC
    }
}

you can pass the active storyboard instance from a controller like this -

let loginVC = Helper.getLoginVC(storyboard: self.storyboard)

In case you are trying to access storyboard from appDelegate or sceneDelegate you can use -

let storyboard = self.window?.rootViewController?.storyboard ?? UIStoryboard.init(name: "Main", bundle: nil)
let loginVC = Helper.getLoginVC(storyboard: storyboard)

let navigationController = UINavigationController.init(rootViewController: loginVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
Ashish P
  • 1,463
  • 1
  • 20
  • 29
0

I have just copy pasted the code form above updated question so that everyone can see it as an answer.

UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:@"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
Imran Khan
  • 317
  • 3
  • 17