Ja͢ck
Ja͢ck

Reputation: 173562

selecting alternative first view controller from story board at application startup

I've just started on iOS programming and so far the tutorials and answers I found here have been a great help to move forward. However, this particular problem has been bumming me all night and I can't find an answer that "feels right".

I'm writing an application that connects to a remote service and the users need to sign in before they can use it. When they start using the application, their first view should be the sign in dialog; when they've authenticated before, they immediately see the overview page.

The project uses story boards - which I think is a great feature - so most of the code that selects and loads the root view controller is already taken care of. I thought the best place to add my logic is the application:didFinishLaunchingWithOptions: method of the AppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
      (NSDictionary *)launchOptions
{
    // select my root view controller here based on credentials present or not
    return YES;
}

But this brought up two questions:

  1. Inside this particular delegate method, the root view controller has already been selected (and loaded?) based on the story board. Could I move to an earlier spot in the loading process to override the first view controller selection or would that needlessly complicate matters?

  2. To override the first view controller I need a reference to the story board, but I couldn't find a better way than to use the storyboardWithName:bundle: constructor of UIStoryboard. That feels wrong, the application should already have a reference to the story board, but how can I access it?

Update

I worked out the second issue I was having, as I found my answer here:

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

NSBundle *bundle = [NSBundle mainBundle];
NSString *sbFile = [bundle objectForInfoDictionaryKey:@"UIMainStoryboardFile"];
UIStoryboard *sb = [UIStoryboard storyboardWithName:sbFile bundle:bundle];

The above will create a new story board instance; to get the active instance, it's a whole lot simpler:

UIStoryboard *sb = [[self.window rootViewController] storyboard];

In the story board file itself you have to set an identifier for the view you wish to load, e.g. LoginDialog. Afterwards you instantiate the view like this:

LoginViewController *login = [sb instantiateViewControllerWithIdentifier:@"LoginDialog"];
[self.window setRootViewController:login];

Within another view controller, the following suffices:

UIStoryboard *sb = self.storyboard;
LoginViewController *login = [sb instantiateViewControllerWithIdentifier:@"LoginDialog"];
[self presentViewController:login animated:NO completion:nil];

Upvotes: 15

Views: 10989

Answers (4)

Ja͢ck
Ja͢ck

Reputation: 173562

With the main storyboard already loaded, it's just a matter of finding its reference so that I can instantiate another root view controller:

UIStoryboard *mainStoryboard = self.window.rootViewController.storyboard;

self.window.rootViewController = [mainStoryboard 
    instantiateViewControllerWithIdentifier:@"view-controller-id"];

Upvotes: -1

Danyun Liu
Danyun Liu

Reputation: 3092

You can just reset the root view controller of the window

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
      (NSDictionary *)launchOptions
{
   if(your_condition) {
       UIViewController *newRoot = [your implementation];
       self.window.rootViewController = newRoot;
   }
   return YES;
}

This is worked for me, Xcode5.0.1

Upvotes: 13

ovidiu
ovidiu

Reputation: 1129

I have a similar scenario as yours. My application uses a UINavigationController as the root view controller. If the user is logged in, I want to present him/her with NotLoggedInViewController, while if it's logged in I want to show the LoggedInViewController.

In a storyboard a UINavigationController can only have one child, so you have to be able to programmatically assign another root view controller to it.

I start by creating a custom navigation controller class, let's name it MyNavigationController. In the storyboard I assign this custom class to the navigation controller object.

Still in the storyboard, I then model both view controllers, and connect one of them to the navigation controller object. Since I need to be able to access them from my code later on, I assign each of them an identifier using the XCode inspector on the right. These identifiers which can be arbitrary strings, but to things simple I just use the class names.

Finally I then implement the viewDidLoad method on MyNavigationController class:

BOOL isLoggedIn = ...;

- (void)viewDidLoad {
  id rootController;
  if (isLoggedIn) {
    rootController = [self.storyboard instantiateViewControllerWithIdentifier:@"LoggedInViewController"];
  } else {
    rootController = [self.storyboard instantiateViewControllerWithIdentifier:@"NotLoggedInViewController"];
  }
  self.viewControllers = [NSArray arrayWithObjects:rootController, nil];
}

Upvotes: 7

hp iOS Coder
hp iOS Coder

Reputation: 3234

I had hardly used storyboard & probably this is not the exact answer to your question. But I will suggest you some way what I did in my project created without using a storyboard.

In didFinishLaunchingWithOptions AuthenticationViewController is the first view loaded. It asks login credentials. Once entered it will enter the actual ViewControllers(viz. TabBar &all..) used by project.

Interesting feature added to project is, when you enter credentials I popped up an UIAleretView that asks user to choose one of the three options.

  1. Save Credentials without passcode
  2. Save Credentials with passcode
  3. Dont save credentials

Here pass code is nothing but 4digit number entered by user. Whenever he wants to 'Save Credentials with passcode', I pushViewController that shows NumberPad instad of default keyboard & popviewController when it finishes entering of pin. If user 'Dont save credentials' & later on while playing the app wants to go for other authentication options then I added the last tab of TabBarController as 'Settings' tab inside which I allow user to choose one of the Authentication options, popped as UIAlertView in the beginning of app start after login.

Dont forget to Save credentials in keychain

In a nutshell,

  1. AuthenticationViewController-> check if login credentials are stored in keychain

1.1. If not stored(i.e. 3. Dont save credentials)-> then show Login page.

1.2. If credentials are saved in keychain-> extract them & see if it is tied with passcode.

1.2.1. If it is tied with passcode(i.e. 2. Save Credentials with passcode )-> then show passcode page.

1.2.2. If it is not tied (1. Save Credentials without passcode)-> then show/load you project's TabBarController hierarchy or other stuff. here actually your app start.

Upvotes: 2

Related Questions