iOS 8 - 设置关键窗口后快速显示视图控制器或解除并立即显示另一个视图控制器时出现故障

时间:2022-04-04 15:00:12

Since testing my app on iOS 8, I find a work around view controllers initialization and presentation really baaadly slow.

自从在iOS 8上测试我的应用程序以来,我发现一个关于视图控制器初始化和演示的工作真的很慢。

I used to work with a code similar to this on iOS 6 & 7:

我曾经在iOS 6和7上使用类似的代码:

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

    [self.window setRootViewController:_rootController];
    [self.window makeKeyAndVisible];

    // Conditions

    if (#first launch condition#) {
        // quite small controller containing Welcome showcase
        WelcomeViewController *w = ....
        [_rootViewController presentViewController:w animated:NO];
    }

    else if (#last opened item condition#) {
        // pretty big container, root view controller contains
        // a grid view which opens Item detail container the same way
        ItemDetailController *item = ....
        [_rootViewController presentViewController:item animated:NO];
    }

}

This became a really sluggish hell with iOS 8. Root view controller now appears visible for 0.5-1 second and then instantly redraws the screen with presented one. Moreover, the slowness of the presentation began to cause an Unbalanced calls to begin/end appearance transitions _rootViewController warning.

在iOS 8中,这变成了一个非常低迷的地狱。根视图控制器现在可以看到0.5-1秒,然后立即重新绘制屏幕,​​显示一个。此外,演示文稿的缓慢开始导致不平衡调用开始/结束外观转换_rootViewController警告。

Initial quick hint was to move both conditions with calls to another function and call it with a zero-delay so it's processed in next main run loop:

最初的快速提示是通过调用另一个函数来移动这两个条件并以零延迟调用它,以便在下一个主运行循环中处理它:

[self performSelector:@selector(postAppFinishedPresentation) withObject:nil afterDelay:0];

or something like that. This fixes the unballanced calls issue, but the visual gap (rootviewcontroller, gap, presented one) becomes (obviously) even bigger.

或类似的东西。这解决了unballanced调用问题,但视觉差距(rootviewcontroller,gap,present one)变得(显然)更大。

The slowness of the presentation is also obvious when you call something usual as:

当您通常称之为以下内容时,演示文稿的缓慢也很明显:

// Example: Delegate caught finished Sign In dialog,
//          dismiss it and instantly switch to Profile controller

-(void)signInViewControllerDidFinishedSuccessfully
{
    [self dismissViewControllerAnimated:NO completion:^{
         UserProfileViewController *userProfile = ...
         [self presentViewController:userProfile animated:NO];
    }];
}

which should be completely fair piece of code, which used to perform direct transition without a visible flick of parent view controller on iOS 7. Now, same thing – parent flicks during the transition, even it's both handled without animation.

这应该是完全公平的代码片段,用于执行直接转换,而不会在iOS 7上显示父视图控制器。现在,同样的事情 - 转换过程中的父动画,即使它都是在没有动画的情况下处理的。

Does anybody face this as an issue? Any solutions? I'd love to solve this without a need to do some hilarious magic with UIWindows for each thing I need to transit flawlessly.

有人认为这是一个问题吗?有解决方案?我很乐意解决这个问题,而不需要为UIWindows做一些搞笑的魔术,因为我需要完美无缺地运送每件东西。

4 个解决方案

#1


1  

I am not sure the requirement is restricted to have root view controller and present anything over there.

我不确定要求是否限制为拥有根视图控制器并在那里显示任何内容。

But according to your code it's have welcome view controller thing and I think in this case, this logic is more useful.

但根据你的代码,它有欢迎视图控制器的东西,我想在这种情况下,这个逻辑更有用。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Conditions

    if (#first launch condition#) {
        // quite small controller containing Welcome showcase
        WelcomeViewController *w = ....

        //It can be navigation or tab bar controller which have "w" as rootviewcontroller
        [self.window setRootViewController:w];
    }

    else if (#last opened item condition#) {
        // pretty big container, root view controller contains
        // a grid view which opens Item detail container the same way
        ItemDetailController *item = ....
        //It can be navigation or tab bar controller which have "item" as rootviewcontroller
        [self.window setRootViewController:item];
    }

    [self.window makeKeyAndVisible];

}

#2


0  

If you use Storyboard, why not try:

如果您使用Storyboard,为什么不尝试:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:@"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
ViewController *_rootController = [storyboard instantiateViewControllerWithIdentifier:@"root"];
[self.window setRootViewController:_rootController];
[self.window makeKeyAndVisible];
if (vcToShow == 1) {
     ViewController2 *w = [storyboard instantiateViewControllerWithIdentifier:@"vc2"];
    [_rootController presentViewController:w animated:NO completion:nil];
}
else if (vcToShow == 2) {
    ViewController2 *w = [storyboard instantiateViewControllerWithIdentifier:@"vc3"];
    [_rootController presentViewController:w animated:NO completion:nil];
}

It looks like there is no delay here.

看起来这里没有延迟。

#3


0  

The delay I had from a dismiss/present pair was fixed by this. It may or may not help your instance. I had to completely change my strategy of displaying/dismissing modal view controllers under iOS 8:

我从解雇/现在对的延迟是由此确定的。它可能会或可能不会帮助您的实例。我不得不完全改变我在iOS 8下显示/解除模态视图控制器的策略:

[_rootViewController presentViewController:vc1 animated:NO completion:nil];

if(iNeedToDisplayVC2) {
     [vc1 presentViewController:vc2 animated:NO completion:nil];
}

So once I am done with vc2 later on, I dismiss both vc1 and vc2 in the same call. This strategy works under iOS 7 as well. I assume earlier versions too but I did not test them.

所以一旦我以后完成vc2,我会在同一个调用中关闭vc1和vc2。此策略也适用于iOS 7。我也假设早期版本,但我没有测试它们。

#4


0  

With iOS8, I've found the old presentation is not quite completed yet in the completion block, and calling dismiss or present right away can lead to console messages and sometimes the present/dismiss does not even occur. I have had some success by adding a further delayed perform to the second presentation:

对于iOS8,我发现在完成块中旧的演示文稿尚未完成,并且立即调用dismiss或present会导致控制台消息,有时甚至不会出现当前/解除。通过在第二个演示文稿中添加进一步延迟执行,我取得了一些成功:

 [self dismissViewControllerAnimated:NO completion:^{
         UserProfileViewController *userProfile = ...
         [[NSOperationQueue mainQueue] addOperationWithBlock:^{
             [self presentViewController:userProfile animated:NO];
         }];
    }];

#1


1  

I am not sure the requirement is restricted to have root view controller and present anything over there.

我不确定要求是否限制为拥有根视图控制器并在那里显示任何内容。

But according to your code it's have welcome view controller thing and I think in this case, this logic is more useful.

但根据你的代码,它有欢迎视图控制器的东西,我想在这种情况下,这个逻辑更有用。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Conditions

    if (#first launch condition#) {
        // quite small controller containing Welcome showcase
        WelcomeViewController *w = ....

        //It can be navigation or tab bar controller which have "w" as rootviewcontroller
        [self.window setRootViewController:w];
    }

    else if (#last opened item condition#) {
        // pretty big container, root view controller contains
        // a grid view which opens Item detail container the same way
        ItemDetailController *item = ....
        //It can be navigation or tab bar controller which have "item" as rootviewcontroller
        [self.window setRootViewController:item];
    }

    [self.window makeKeyAndVisible];

}

#2


0  

If you use Storyboard, why not try:

如果您使用Storyboard,为什么不尝试:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:@"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
ViewController *_rootController = [storyboard instantiateViewControllerWithIdentifier:@"root"];
[self.window setRootViewController:_rootController];
[self.window makeKeyAndVisible];
if (vcToShow == 1) {
     ViewController2 *w = [storyboard instantiateViewControllerWithIdentifier:@"vc2"];
    [_rootController presentViewController:w animated:NO completion:nil];
}
else if (vcToShow == 2) {
    ViewController2 *w = [storyboard instantiateViewControllerWithIdentifier:@"vc3"];
    [_rootController presentViewController:w animated:NO completion:nil];
}

It looks like there is no delay here.

看起来这里没有延迟。

#3


0  

The delay I had from a dismiss/present pair was fixed by this. It may or may not help your instance. I had to completely change my strategy of displaying/dismissing modal view controllers under iOS 8:

我从解雇/现在对的延迟是由此确定的。它可能会或可能不会帮助您的实例。我不得不完全改变我在iOS 8下显示/解除模态视图控制器的策略:

[_rootViewController presentViewController:vc1 animated:NO completion:nil];

if(iNeedToDisplayVC2) {
     [vc1 presentViewController:vc2 animated:NO completion:nil];
}

So once I am done with vc2 later on, I dismiss both vc1 and vc2 in the same call. This strategy works under iOS 7 as well. I assume earlier versions too but I did not test them.

所以一旦我以后完成vc2,我会在同一个调用中关闭vc1和vc2。此策略也适用于iOS 7。我也假设早期版本,但我没有测试它们。

#4


0  

With iOS8, I've found the old presentation is not quite completed yet in the completion block, and calling dismiss or present right away can lead to console messages and sometimes the present/dismiss does not even occur. I have had some success by adding a further delayed perform to the second presentation:

对于iOS8,我发现在完成块中旧的演示文稿尚未完成,并且立即调用dismiss或present会导致控制台消息,有时甚至不会出现当前/解除。通过在第二个演示文稿中添加进一步延迟执行,我取得了一些成功:

 [self dismissViewControllerAnimated:NO completion:^{
         UserProfileViewController *userProfile = ...
         [[NSOperationQueue mainQueue] addOperationWithBlock:^{
             [self presentViewController:userProfile animated:NO];
         }];
    }];