在 didFinishLaunching 上自定义视图控制器堆栈

Posted

技术标签:

【中文标题】在 didFinishLaunching 上自定义视图控制器堆栈【英文标题】:Customize View Controller Stack on didFinishLaunching 【发布时间】:2015-02-20 22:35:04 【问题描述】:

我正在尝试使用以下代码在首次启动时为我的应用提供教程/登录/介绍视图控制器:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    UIViewController *firstController = [[UIViewController alloc] init];
    [self.window setRootViewController:firstController];

    if ([self shouldShowIntro]) 
        IntroViewController *introViewController = [[IntroViewController alloc] init];
        [firstController presentViewController:introViewController animated:NO completion:nil];
    

    return YES;

这很好用,但是会出现令人讨厌的视觉效果...在您看到IntroViewController 之前,有一瞬间firstController 是可见的。我尝试在设置窗口的 rootViewController 之前显示IntroViewController,但这(不足为奇)会导致以下警告:

Warning: Attempt to present <IntroViewController: 0x7fd8eb3362f0> on <UIViewController: 0x7fd8eb335180> whose view is not in the window hierarchy!

如何在没有这种烦人的视觉闪光的情况下以模态方式呈现 IntroViewController?我希望IntroViewController 在启动屏幕消失后已经显示并且能够以模态方式关闭。

【问题讨论】:

您针对/测试的 ios 版本是什么?看起来你没有使用故事板,对吧? 我没有使用故事板。我目前正在 iOS 8.1 上进行测试,但希望该解决方案也能在 iOS 7 上运行(或 iOS 7 的替代解决方案)。 你能发布一个展示闪烁的例子吗?即使添加一个带有少量控件的视图控制器也不会对我造成任何闪烁。 看看这个类似的问题:***.com/a/26856676/1049134 为什么不将登录/介绍控制器作为根启动,而不是尝试以模态方式显示它。当他们登录/完成时,通过您提供的方法要求应用程序委托将根切换到您的正常导航树。您可以在情节提要中强制执行此操作,方法是将您的登录设置为初始视图控制器,这将使从启动屏幕到登录的顺利移动。 【参考方案1】:

以下内容适用于我,在 iPhone 6 和模拟器上的 iOS 8.1 上运行时没有闪烁。我使用的是 SDK 版本 8.1,我认为您的 SDK 级别不同,因为我得到的警告和结果与使用您提供的代码不同。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    UIViewController *firstController = [[UIViewController alloc] init];
    firstController.view.backgroundColor = [UIColor blueColor];
    [self.window setRootViewController:firstController];

    dispatch_async(dispatch_get_main_queue(), ^
        UIViewController* modalViewController = [[UIViewController alloc] init];
        modalViewController.view.backgroundColor = [UIColor grayColor];
        [firstController presentViewController: modalViewController animated: NO completion: nil];
    );

    // dismiss after a few seconds..
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
        [firstController dismissViewControllerAnimated: YES completion: nil];
    );

    return YES;

更新与MMDrawerController:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    MMDrawerController *drawerController = [[MMDrawerController alloc] init];
    drawerController.centerViewController = [[UIViewController alloc] init];
    drawerController.centerViewController.view.backgroundColor = [UIColor blueColor];
    drawerController.rightDrawerViewController = [[UIViewController alloc] init];
    drawerController.rightDrawerViewController.view.backgroundColor = [UIColor greenColor];
    drawerController.openDrawerGestureModeMask = MMOpenDrawerGestureModeAll;
    drawerController.closeDrawerGestureModeMask = MMOpenDrawerGestureModeAll;

    [self.window setRootViewController:drawerController];

    dispatch_async(dispatch_get_main_queue(), ^
        UIViewController* modalViewController = [[UIViewController alloc] init];
        modalViewController.view.backgroundColor = [UIColor grayColor];
        [drawerController presentViewController: modalViewController animated: NO completion: nil];
    );

    // dismiss after a few seconds..
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
        [drawerController dismissViewControllerAnimated: YES completion: nil];
    );

    return YES;

此外,根据您的评论,听起来好像预加载视图是关键。在演示之前尝试调用 [firstController view] 和 [modalController view] 以确保它们已加载。

【讨论】:

这适用于我使用标准 UIViewControllers,但是当我为我的子类控制器切换 UIViewController 时,我仍然看到闪烁。这可能与我的 firstController 是 MMDrawerController (github.com/mutualmobile/MMDrawerController) 有关。我在 MMDrawerController 中收到“开始/结束外观转换的不平衡调用”错误。我会调查一下。 另外,当使用标准 UIViewControllers 时,调度似乎不是必需的。确认,我认为我的问题与我设置 MMDrawerController 的方式有关,而不是与我尝试设置 UIViewController 堆栈的方式有关。哎呀。 如果没有 dispatch_async 我会收到 unbalanced-calls 消息。 啊,你是对的。然而,我注意到了另一件事。当我将此代码与具有纯色背景颜色的简单 UIViewControllers 一起使用时,它可以工作。当 modalViewController 是一个更复杂的控制器(可能需要更长的加载时间)时,我仍然会看到闪烁。 尝试添加 [firstController view] 和 [modalController view] 以在呈现前预加载视图。【参考方案2】:

尝试将此行移到设置视图控制器的代码下方。

[self.window makeKeyAndVisible];

类似这样的:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];

    UIViewController *firstController = [[UIViewController alloc] init];
    [self.window setRootViewController:firstController];

    if ([self shouldShowIntro]) 
        IntroViewController *introViewController = [[IntroViewController alloc] init];
        [firstController presentViewController:introViewController animated:NO completion:nil];
    

    [self.window makeKeyAndVisible];

    return YES

【讨论】:

在呈现过程中似乎firstController仍然不可见【参考方案3】:

if ([self shouldShowIntro]) 
    IntroViewController *introViewController = [[IntroViewController alloc] init];
    [firstController presentViewController:introViewController animated:NO completion:nil];

在 FirstViewController 的 viewDidAppear 函数中,而不是在 AppDelegate 中。那应该抑制警告并顺利执行操作。

【讨论】:

以上是关于在 didFinishLaunching 上自定义视图控制器堆栈的主要内容,如果未能解决你的问题,请参考以下文章

在 iframe 上自定义 css [重复]

使用 Swift 3 在 UICollectionView 上自定义布局

如何在 sylius 上自定义密码验证器?

在弹性 beantalk 上自定义 nginx.conf

如何在 Worklight 上自定义直接更新消息?

在 Android 4 上自定义 ActionBar 选项卡