xcode 应用程序 didFinishLaunchingWithOptions 在故事板加载完成之前被调用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16257291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 03:17:34  来源:igfitidea点击:

application didFinishLaunchingWithOptions is called before Storyboards are done loading?

iphoneobjective-cxcodeuistoryboardfacebook-login

提问by Fateh Khalsa

I have an app that implements a Facebook login at startup, using code pretty much exactly similar to this: Facebook Scrumptious Tutorial, except with Storyboards.

我有一个在启动时实现 Facebook 登录的应用程序,使用的代码与此非常相似:Facebook Scrumptious Tutorial,除了 Storyboards。

The basic gist of the code is that on app startup the app delegate checks to see if you're logged in already, and if you are it goes straight to the main view, and if not, it asks the main view to present a login view so the user can log in.

代码的基本要点是,在应用程序启动时,应用程序委托检查您是否已经登录,如果您已经登录,它会直接进入主视图,如果没有,它会要求主视图显示登录信息查看以便用户可以登录。

I've already solved my other issue with being able to tell the main view in the storyboard to present a different view, by getting the main view from the view heirarchy, and then calling a segue on the view. That all works fine, however I've got one last problem to solve:

通过从视图层次结构中获取主视图,然后在视图上调用 segue,我已经解决了我的另一个问题,即能够告诉故事板中的主视图呈现不同的视图。一切正常,但是我还有最后一个问题需要解决:

As far as I'm aware, the application didFinishLaunchingWithOptionsmethod is supposedto be called afterthe storyboards have been fully loaded. However in my code, if I try to tell the main view to present another view, it gives me an error basically saying it isn't loaded yet (Warning: Attempt to present < QLoginViewController: 0x955c020> on < UINavigationController: 0xa28c6e0> whose view is not in the window hierarchy!).

据我所知,应该在故事板完全加载调用应用程序 didFinishLaunchingWithOptions方法。但是在我的代码中,如果我试图告诉主视图呈现另一个视图,它会给我一个错误,基本上说它尚未加载(警告:尝试在 < UINavigationController: 0xa28c6e0> 上呈现 < QLoginViewController: 0x955c020> 其视图不在窗口层次结构中!)。

If I tell it to present the view after a delay however:

但是,如果我告诉它在延迟后呈现视图:

[self performSelector:@selector(showLoginViewAnimated:) withObject:NO afterDelay:.001];

(where showLoginViewAnimated:is the method that tells the main view to present the login view), then it works fine.

(其中showLoginViewAnimated:是告诉主视图呈现登录视图的方法),然后它工作正常。

Can anyone help me figure out what's going wrong here, and how I might be able to fix it? Performing the selector with a delay is obviously a bad workaround, as I can never know if a different device might take longer to load the views...

任何人都可以帮助我弄清楚这里出了什么问题,以及我如何能够解决它?延迟执行选择器显然是一个糟糕的解决方法,因为我永远不知道不同的设备是否可能需要更长的时间来加载视图......

Here is my appDelegate didFinishLaunchingWithOptionscode:

这是我的 appDelegate didFinishLaunchingWithOptions代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    self.navigationController = (UINavigationController *)self.window.rootViewController;

    // Navigation Bar Color
    [[UINavigationBar appearance] setTintColor:[UIColor colorWithRed:255.0/255.0 green:128.0/255.0 blue:60.0/255.0 alpha:1.0]];


    /* Facebook Login */    // THIS IS THE RELEVANT CODE: 
    // See if we have a valid token for the current state
    if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
        // Yes, valid token exists - open the session (don't display login page)
        [self openSession];
    } else {
        // No, valid token does not exist -  display the login page.
        if ([self.navigationController isViewLoaded]) {
            [self showLoginViewAnimated:NO];   // MY ATTEMPT TO AVOID USING THE DELAY IF POSSIBLE
        }
        else {
            [self performSelector:@selector(showLoginViewAnimated:) withObject:NO afterDelay:.001]; // Delay needed to allow for storyboard loading
        }
    }

    return YES;
}

And here is the showLoginViewAnimated:code:

这是showLoginViewAnimated:代码:

- (void)showLoginViewAnimated:(BOOL)animated
{
    UIViewController *topViewController = [self.navigationController topViewController];
    UIViewController *presentedViewController = [topViewController presentedViewController];

    // If the login screen is not already displayed, display it. If the login screen is
    // displayed, then getting back here means the login in progress did not successfully
    // complete. In that case, notify the login view so it can update its UI appropriately.

    if (![presentedViewController isKindOfClass:[QLoginViewController class]]) {
        if (animated) {
            [topViewController performSegueWithIdentifier:@"ShowLoginViewAnimated" sender:self];
        }
        else {
            [topViewController performSegueWithIdentifier:@"ShowLoginViewStatic" sender:self];
        }
    }
    else {
        QLoginViewController *loginViewController = (QLoginViewController *)presentedViewController;
        [loginViewController loginFailed];
    }
}

In the original, non-storyboard version of the app, the showLoginViewAnimated:method wouldn't be called in application didFinishLaunchingWithOptionsuntil I had already manually created the views like so:

在应用程序的原始非故事板版本中,在我手动创建视图之前,不会在应用程序 didFinishLaunchingWithOptions 中调用showLoginViewAnimated:方法,如下所示

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];
self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];

self.window.rootViewController = self.navController;
[self.window makeKeyAndVisible];

So I would already know 100% for sure that the views exist before I try to tell them to present new views. In the storyboard version of the app however, none of that code exists, so I simply have to trust that theoretically the application didFinishLaunchingWithOptionsmethod isn't called until the storyboard views are fully loaded - however, that doesn't seem to be the case. Perhaps it is doing it asynchronously instead? I have no idea...

因此,在我尝试告诉他们呈现新视图之前,我已经 100% 确定这些视图存在。然而,在应用程序的故事板版本中,这些代码都不存在,所以我只需要相信理论上应用程序 didFinishLaunchingWithOptions方法在故事板视图完全加载之前不会被调用 - 但是,情况似乎并非如此. 也许它是异步执行的?我不知道...

Any ideas? Thanks for any help!

有任何想法吗?谢谢你的帮助!

EDIT:Here is the original code from the tutorial, which works perfectly fine - and does almost exactly the same thing, just with nibs. I've added the (BOOL)animatedparameter to the showLoginViewmethod in my code, but that's for something else and doesn't change anything (I've checked).

编辑:这是教程中的原始代码,它运行良好 - 并且几乎完全相同,只是使用笔尖。我已经在我的代码中向showLoginView方法添加了(BOOL)animated参数,但这是为了其他东西并且不会改变任何东西(我已经检查过)。

Here's the original (non-storyboard) appDelegate didFinishLaunchingWithOptionsmethod:

这是原始(非故事板) appDelegate didFinishLaunchingWithOptions方法:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[FBLViewController alloc] initWithNibName:@"FBLViewController" bundle:nil];
    self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];

    self.window.rootViewController = self.navController;
    [self.window makeKeyAndVisible];


    // See if we have a valid Facebook token for the current state
    if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
        // Yes, so just open the session (this won't display any UX).
        [self openSession];
    }
    else {
        // No, display the login page.
        [self showLoginView];
    }

    return YES;
}

And here's the original (non-storyboard) showLoginViewmethod:

这是原始(非故事板)showLoginView方法:

- (void)showLoginView
{
    UIViewController *topViewController = [self.navController topViewController];
    UIViewController *presentedViewController = [topViewController presentedViewController];

    // IF the login screen is not already displayed, display it. If the login screen is
    // displayed, then getting back here means the login in progress did not successfully
    // complete. In that case, notify the login view so it can update its UI appropriately.

    if (![presentedViewController isKindOfClass:[FBLLoginViewController class]]) {
        FBLLoginViewController *loginViewController = [[FBLLoginViewController alloc] initWithNibName:@"FBLLoginViewController" bundle:nil];
        loginViewController.delegate = self;
        [topViewController presentViewController:loginViewController animated:NO completion:nil];
    }
    else {
        FBLLoginViewController *loginViewController = (FBLLoginViewController *)presentedViewController;
        [loginViewController loginFailed];
    }
}

回答by Fateh Khalsa

Add this line to didFinishLaunchingWithOptions:

将此行添加到didFinishLaunchingWithOptions

[self.window makeKeyAndVisible];

before the Facebook login code.

在 Facebook 登录代码之前。

回答by matt

The main storyboard is loaded, but the view controllers still work just as they did before: they do not exist until needed, and then they have to load their views just as before. Your code still needs to be in a view controller's viewDidLoador viewWillAppearor whatever just as if you were using nibs, creating view controllers in code.

主故事板已加载,但视图控制器仍然像以前一样工作:它们在需要时才存在,然后它们必须像以前一样加载它们的视图。你的代码仍然需要在一个视图控制器viewDidLoadviewWillAppear或什么,就像如果你使用笔尖,在代码中创建视图控制器。