如何使用故事板呈现飞溅/login视图控制器

我以各种forms看到了这个问题,没有一个明确的答案。 我要在这里问和回答。 我的应用程序需要在启动… inits,一些networking调用,login等工作。我不希望我的主视图控制器工作,直到完成。 这样做有什么好的模式?

要求:

  • iOS5 +,故事板,ARC。
  • 主VC的视图不能出现,直到启动工作完成。
  • 当启动工作完成时,需要select转换样式到主vc。
  • 想要在故事板中尽可能多的布局。
  • 代码应该清晰地包含在某个明显的地方。

编辑,20177月自第一次写作以来,我改变了我的做法,把自己的视图控制器的启动任务。 在那个VC中,我检查启动条件,根据需要提供一个“繁忙的”UI等。根据启动时的状态,我设置窗口的根VC。

除了解决OP问题之外,此方法还具有更好地控制启动UI和UI转换的额外好处。 以下是如何做到这一点:

在您的主要故事板中,添加一个名为LaunchViewController的新VC,并将其作为应用程序的初始vc。 给你的应用程序的“真实”初始vc一个标识符,如“AppUI”(标识符在IB中的标识选项卡上)。

找出其他主要用户界面stream程的开始(例如注册/login,教程等),并提供这些描述性标识符。 (有些人喜欢把自己的故事板保持在每个stream程中,这是一个很好的做法,IMO)。

另一个很好的可选的想法 :也给你的应用程序的启动故事板的vc一个标识符(如“LaunchVC”),以便您可以抓住它,并在启动时使用它的视图。 这将为用户在启动期间和执行启动任务时提供无缝的体验。

这是我的LaunchViewController看起来像….

 @implementation LaunchViewController - (void)viewDidLoad { [super viewDidLoad]; // optional, but I really like this: // cover my view with my launch screen's 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's 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's 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 

下面的原始答案,但我更喜欢我目前的做法

让我们表示应用程序准备运行一个布尔types的主要vc,如下所示:

 BOOL readyToRun = startupWorkIsDone && userIsLoggedIn; 
  1. 创build一个AppStartupViewController并将其放在应用程序故事板中。
  2. 不要拖动任何segue到它,并且不要使它凝视vc,只是让它漂浮在某处。
  3. 在故事板的vc属性检查器中,将其标识符设置为“AppStartupViewController”。

在AppStartupViewController.m中,当readyToRun条件满足时,它可以消除:

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

现在,只要应用程序变为活动状态,它就可以检查运行的准备情况,并在需要时显示AppStartupViewController。 在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's view } } 

这主要是答案,但有一个困难。 不幸的是,主要的vc被加载(没关系)并且在AppStartupViewController被呈现之前得到viewWillAppear:消息(不好)。 这意味着我们必须在MainViewController.m中传播一些额外的启动逻辑,像这样:

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

我希望这是有帮助的。

使用导航控制器的另一个解决scheme。

  1. 您的导航控制器是初始视图控制器,请将您的主控制器设置为导航控制器根视图。

  2. 将你的加载控制器添加到故事板,并链接到一个风格模式的命名segue。

  3. 在您的主控制器的viewWillAppear触发segue(每个应用程序只运行一次)。

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

如果您在viewDidLoad中触发了segue,则可能是因为导航控制器的animation尚未完成,您将收到unbalanced calls to begin/end appearance transitions