How to introduce a popup / login controller using a storyboard

I saw this question in different forms without a clear answer. I am going to ask and answer here. My application needs to do work at startup ... inits, multiple network calls, login, etc. I do not want my main view controller to work before that. What is a good circuit for this?

Requirements:

  • iOS5 +, storyboards, ARC.
  • The main view of vc cannot appear until the startup work has been completed.
  • a choice of transition styles to the main vc is required when performing startup work.
  • want to make as much layout as possible in the storyboard.
  • the code should be kept clean somewhere obvious.
+4
ios iphone storyboard appdelegate
source share
2 answers

EDIT, July 2017. Starting from the first writing, I changed my practice to one where I run tasks on my own view controller. In this VC, I check the launch conditions, if necessary, click the busy interface, etc. Based on the state at startup, I set the root of the VC window.

In addition to solving the OP problem, this approach has additional advantages that allow better control over the launch of UI and UI transitions. Here's how to do it:

In the main storyboard, add a new VC called LaunchViewController and make it the initial application vc. Give your application a "real" initial vc identifier of the type "AppUI" (the identifiers are on the "Identification" tab in IB).

Identify other vcs that are the launches of the main user interface threads (e.g. Registration / Login, Tutorial, etc.) and also give these descriptive identifiers. (Some people prefer to keep each thread in their own storyboard. This is good practice, IMO).

Another nice additional idea: give your application the launch of the vc ID storyboard too (for example, β€œLaunchVC”) so that you can capture it and use it during startup. This will provide a seamless experience for the user during startup and during the execution of your startup tasks.

This is what my LaunchViewController looks like ....

 @implementation LaunchViewController - (void)viewDidLoad { [super viewDidLoad]; // optional, but I really like this: // cover my view with my launch screen view for a seamless start UIStoryboard *storyboard = [self.class storyboardWithKey:@"UILaunchStoryboardName"]; UINavigationController *vc = [storyboard instantiateViewControllerWithIdentifier:@"LaunchVC"]; [self.view addSubview:vc.view]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self hideBusyUI]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self showBusyUI]; // start your startup logic here: // let say you need to do a network transaction... // [someNetworkCallingObject doSomeNetworkCallCompletion:^(id result, NSError *error) { if (/* some condition */) [self.class presentUI:@"AppUI"]; else if (/* some condition */) [self.class presentUI:@"LoginUI"]; // etc. }]; } #pragma mark - Busy UI // optional, but maybe you want a spinner or something while getting started - (void)showBusyUI { // in one app, I add a spinner on my launch storyboard vc // give it a tag, and give the logo image a tag, too // here in animation, I fade out the logo and fade in a spinner UIImageView *logo = (UIImageView *)[self.view viewWithTag:32]; UIActivityIndicatorView *aiv = (UIActivityIndicatorView *)[self.view viewWithTag:33]; [UIView animateWithDuration:0.5 animations:^{ logo.alpha = 0.0; aiv.alpha = 1.0; }]; } - (void)hideBusyUI { // an animation that reverses the showBusyUI } #pragma mark - Present UI + (void)presentUI:(NSString *)identifier { UIStoryboard *storyboard = [self storyboardWithKey:@"UIMainStoryboardFile"]; UINavigationController *vc = [storyboard instantiateViewControllerWithIdentifier:identifier]; UIWindow *window = [UIApplication sharedApplication].delegate.window; window.rootViewController = vc; // another bonus of this approach: any VC transition you like to // any of the app main flows [UIView transitionWithView:window duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:nil]; } + (UIStoryboard *)storyboardWithKey:(NSString *)key { NSBundle *bundle = [NSBundle mainBundle]; NSString *storyboardName = [bundle objectForInfoDictionaryKey:key]; return [UIStoryboard storyboardWithName:storyboardName bundle:bundle]; } @end 

Original answer below, although I prefer my current approach

Let it express the readiness of the application to launch the main vc with a logical one, something like:

 BOOL readyToRun = startupWorkIsDone && userIsLoggedIn; 
  • Create an AppStartupViewController and place it in the application storyboard.
  • Do not drag any segue onto it and do not do it vc ving, just leave it floating somewhere.
  • In the vc attribute inspector in the storyboard, set it as "AppStartupViewController".

In AppStartupViewController.m, when the readyToRun conditions are met, it can reject itself:

 self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; // your choice here from UIModalTransitionStyle [self dismissViewControllerAnimated:YES completion:nil]; 

Now that the application is becoming active, it can check its readiness for launch and present the AppStartupViewController, if necessary. In AppDelegate.h

 - (void)applicationDidBecomeActive:(UIApplication *)application { BOOL readyToRun = startupWorkIsDone && userIsLoggedIn; if (!readyToRun) { UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; AppStartupViewController *startupVC = [storyboard instantiateViewControllerWithIdentifier:@"AppStartupViewController"]; [self.window.rootViewController presentViewController:startupVC animated:NO completion:nil]; // animate = NO because we don't want to see the mainVC view } } 

This is basically the answer, but there is one hitch. Unfortunately, the main vc loads (this is good) and receives the message viewWillAppear: (not everything is ok) before the AppStartupViewController is presented. This means that in MainViewController.m you need to add a little extra startup logic:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (readyToRun) { // the view will appear stuff i would have done unconditionally before } } 

I hope this will be helpful.

+13
source share

Another solution when using a navigation controller.

  • The navigation controller, which is the initial view controller, sets the primary controller as the root view of the navigation controller.

  • Add a boot controller to the storyboard and associate it with the modal style segue name.

  • In your main controller view, WillAppear launches segue (only once to run the application).

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if(_isFirstRun) { _isFirstRun = NO; [self performSegueWithIdentifier:@"segueLoading" sender:nil]; } } 

This will not work if you call segue in viewDidLoad, probably because the animation of the navigation controller is not finished yet and you get unbalanced calls to begin/end appearance transitions

0
source share

All Articles