如何使用故事板呈现启动/登录视图控制器 [英] How to present a splash/login view controller using storyboards

查看:92
本文介绍了如何使用故事板呈现启动/登录视图控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在没有明确答案的情况下以各种形式看到了这个问题。我要在这里问一下这个问题。我的应用程序需要在启动时工作... inits,一些网络调用,登录等。我不希望我的主视图控制器工作,直到完成。这样做的好方法是什么?

I've seen this question in various forms without a clear answer. I'm going to ask and answer here. My app needs to do work on startup... inits, a few network calls, login, etc. I don't want my main view controller working until that's done. What's a good pattern for doing this?

要求:


  • iOS5 +,故事板,ARC。

  • 主要的vc视图在启动工作完成之前不会出现。

  • 想要选择过渡样式到主vc时启动工作已经完成。

  • 想在storyboard中尽可能多地进行布局。

  • 代码应该干净地包含在显而易见的地方。

  • iOS5+, storyboards, ARC.
  • the main vc's view cannot appear until the startup work is done.
  • want a choice of transition styles to the main vc when the startup work is done.
  • want to do as much layout as possible in storyboard.
  • the code should be cleanly contained somewhere obvious.

推荐答案

编辑,2017年7月
自第一次写作以来,我已将我的练习改为将我的启动任务交给他们自己的视图控制器。在那个VC中,我检查启动条件,如果需要,提供忙UI等。根据启动时的状态,我设置窗口的根VC。

EDIT, July 2017 Since the first writing, I've changed my practice to one where I give the start up tasks to their own view controller. In that VC, I check startup conditions, present a "busy" UI if needed, etc. Based on the state at startup, I set the window's root VC.

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

In addition to solving the OP problem, this approach has the additional benefits of giving better control of the startup UI and UI transitions. Here's how to do it:

在主故事板中,添加一个名为 LaunchViewController 的新VC并将其设为应用程序的初始vc。为您的应用程序的真实初始vc提供一个标识符,如AppUI(标识符位于IB中的标识选项卡上)。

In your main storyboard, add a new VC, called LaunchViewController and make it the app's initial vc. Give your app's "real" initial vc an identifier like "AppUI" (identifiers are on the Identity tab in IB).

识别作为主UI启动的其他vcs流程(例如注册/登录,教程等)并提供这些描述性标识符。 (有些人喜欢将每个流程保留在自己的故事板中。这是一个很好的做法,IMO)。

Identify other vcs that are the starts of main UI flows (e.g. Signup/Login, Tutorial, and so on) and give these descriptive identifiers, too. (Some prefer to keep each flow in it's own storyboard. That's a good practice, IMO).

另一个不错的可选想法:给你app的启动故事板的vc也是一个标识符(比如LaunchVC),这样你就可以抓住它并在启动时使用它的视图。这将为用户在启动期间和执行启动任务时提供无缝体验。

One other nice optional idea: give your app's launch storyboard's vc an identifier, too (like "LaunchVC"), so that you can grab it and use it's view during startup. This will provide a seamless experience for the user during launch and while you do your startup tasks.

这是我的 LaunchViewController 看起来像....

Here's what my LaunchViewController looks like....

@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

原始答案下面,虽然我更喜欢我当前的方法

让我们表达应用程序准备好用布尔运行主vc,如:

Let's express the app's readiness to run the main vc with a boolean, something like:

BOOL readyToRun = startupWorkIsDone && userIsLoggedIn;




  1. 创建一个AppStartupViewController并将其放在app storyboard中。

  2. 不要将任何segue拖到它上面,也不要让它成为盯着vc,只是让它漂浮在某个地方。

  3. 在故事板中的vc属性检查器中,将其标识符设置为AppStartupViewController。

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

In the AppStartupViewController.m, when the readyToRun conditions have been met, it can dismiss itself:

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

现在,只要应用程序变为活动状态,它就可以检查是否准备好运行,并提供AppStartupViewController这是必要的。
在AppDelegate.h中

Now, whenever the app becomes active, it can check for readiness to run, and present the AppStartupViewController if it's needed. 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's view
    }
}

这主要是答案,但有一个障碍。不幸的是,在呈现AppStartupViewController之前,主vc被加载(没关系)并获得viewWillAppear:消息(不行)。这意味着我们必须在MainViewController.m中传播一些额外的启动逻辑,如下所示:

That's mostly the answer, but there is one hitch. Unfortunately the main vc gets loaded (that's okay) and gets a viewWillAppear: message (not okay) before the AppStartupViewController is presented. It means we have to spread a little extra startup logic, like this, in MainViewController.m:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if (readyToRun) {
        // the view will appear stuff i would have done unconditionally before
    }
}

我希望这会有所帮助。

这篇关于如何使用故事板呈现启动/登录视图控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆