The exact moment iOS takes the view snapshot when entering background?

僤鯓⒐⒋嵵緔 提交于 2019-11-28 18:21:01

The screenshot is taken immediately after this method returns. I guess your -resetAnimation method completes in the next runloop cycle and not immediately. I've not tried this, but you could try to let the runloop run and then return a little bit later:

- (void) applicationDidEnterBackground:(UIApplication *)application {
    // YOUR CODE HERE

    // Let the runloop run for a brief moment
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}

I hope this helps, Fabian


Update: -pauseAnimation and -resetAnimation distinction

Approach: Delay the animation happening in -applicationWillResignActive: and cancel the delayed animation in -applicationDidEnterBackground:

- (void) applicationWillResignActive:(UIApplication *)application {
    // Measure the time between -applicationWillResignActive: and -applicationDidEnterBackground first!
    [self performSelector:@selector(pauseAnimation) withObject:nil afterDelay:0.1];

    // OTHER CODE HERE
}

- (void) applicationDidEnterBackground:(UIApplication *)application {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pauseAnimation) object:nil];

    // OTHER CODE HERE
}

I've now run some tests, and eliminated the problem, thanks to @Fabian Kreiser.

To conclude: Kreiser had it right: iOS takes the screenshot immediately after the method applicationDidEnterBackground: returns -- immediately meaning, before the end of the current runloop.

What this means is, if you launch any scheduled tasks in the didEnterBackground method you want to finish before leaving, you will have to let the current runloop run for as long as the tasks might take to finish.

In my case, the scheduled task was an UIAnimateWithDuration method call -- I let myself be confused by the fact that both its delay and duration was 0 -- the call was nonetheless scheduled to run in another thread, and thus wasn't able to finish before the end of applicationDidEnterBackground method. Result: the screenshot was indeed taken before the display was updated to the state I wanted -- and, when relaunching, this screenshot flashed briefly onscreen, causing the unwanted flickering.

Furthermore, to provide the "smooth" vs. "instant" transition behavior explained in my question, Kreiser's suggestion to delay the "smooth" transition call in applicationWillResignActive: and cancel the call in applicationDidEnterBackground: works fine. I noticed the delay between the two delegate methods was around 0.005-0.019 seconds in my case, so I applied a generous margin and used a delay of 0.05 seconds.

My bounty, the correct answer tick, and my thanks go to Fabian. Hopefully this helps others in similar situation, too.

The runloop solution actually results in some problems with the app.

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];

If you go to the background and immediately open the app again, the app will turn into a black screen. When you reopen the app for the second time, everything is back to normal.

A better way is to use

[CATransaction flush]

This forces all current transactions to be immediately applied and does not have the problem resulting in a black screen.

Depending on how hardcore important it is to you to have this transition run smoothly, you could kill off multi-tasking for your app entirely w/ UIApplicationExitsOnSuspend. Then, you would be guaranteed your Default.png and a clean visual state.

Of course, you'd have to save/restore state on exit/startup, and without more info on the nature of your app, it's tough to say whether this would be worth the trouble.

vedrano

In iOS 7, there is [[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch] call that does exactly what you needed.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!