UIStoryboard: What's the Correct Way to Get the Active Storyboard?
Asked Answered
S

5

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?

Southeasterly answered 24/3, 2012 at 16:59 Comment(7)
That's the main storyboard, not the "active" storyboard, whatever that means. How many storyboards are in your app?Octet
2. One for iPhone, and one for iPad. This works fine. Only one is active at a time.Southeasterly
Oh right, I get you. You should add your code as an answer, then.Octet
I'm too new. I'll add it later, when SO lets me, so I edited the original.Southeasterly
An easier way, if you are trying to get this from an existing view controller that was loaded from the storyboard (very common) is simply: self.storyboardDioecious
Thanks! In this case, that may be more appropriate.Southeasterly
Your answer pushed me in the right direction :) the code I'm using now is UIStoryboard *sb = [[self.window rootViewController] storyboard];Depilate
S
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]];
Southeasterly answered 25/3, 2012 at 3:17 Comment(6)
calling [UIStoryboard storyboardWithName:... creates a new instance of the storyboard. This won't give you access to the same instances that are actually loaded.Jarv
Thanks. Yeah, but that's OK. I want a new instance. The already loaded one is off doing something else.Southeasterly
Definitely the best answer for my problem. Thanks!Prole
Or may be adding this to the AppDelegate as a Class method, so you can reuse the storyboard and avoid creating unnecessary instances.Stanstance
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?Southeasterly
@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 systemChapa
O
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.

Oedema answered 25/6, 2012 at 5:35 Comment(1)
+1 for the .storyboard property. This property is not only available for a navigation controller but also for UITableViewController, UITabBarController, UICollectionViewController, etc.Idiographic
S
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]];
Southeasterly answered 25/3, 2012 at 3:17 Comment(6)
calling [UIStoryboard storyboardWithName:... creates a new instance of the storyboard. This won't give you access to the same instances that are actually loaded.Jarv
Thanks. Yeah, but that's OK. I want a new instance. The already loaded one is off doing something else.Southeasterly
Definitely the best answer for my problem. Thanks!Prole
Or may be adding this to the AppDelegate as a Class method, so you can reuse the storyboard and avoid creating unnecessary instances.Stanstance
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?Southeasterly
@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 systemChapa
H
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
}
Heldentenor answered 20/7, 2016 at 15:40 Comment(0)
H
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()
Hydrosol answered 16/10, 2022 at 21:33 Comment(0)
A
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]];
Aminaamine answered 21/6, 2016 at 9:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.