Why the OS calls your view again after the app is already in the background
When the user presses the Home button iOS
Those last layout passes are not there so that you can render with
OpenGL again – they are only there so that you can put ordinary UIKit
content in the hierarchy if you want the system to take an additional
snapshot that looks better than the one it already has.
Because Core Animation will not rasterise a CAEAGLLayer that has no
current contents, the second (or third…) snapshot often ends up being a
scaled version of the first one. That is what you are seeing.
The rule “Background Apps May Not Execute Commands on the Graphics Hardware” is therefore not contradicted: the system is giving you the opportunity to substitute some non–OpenGL content before it takes those extra pictures; it is not asking you to draw with OpenGL.
What shipping OpenGL games do
Almost every game that looks good in the App-Switcher does exactly this:
• Just before it stops using OpenGL it puts an ordinary UIKit view –
most of the time an UIImageView that contains a prerendered picture –
on top of the GL view.
• When the game becomes active again it simply removes that UIImageView
and starts using OpenGL once more.
Because the overlay is a normal UIKit view, Core Animation can render it again and again during the additional “pre-snapshot” layouts and the snapshot that iOS stores for each orientation is sharp.
Step-by-step recipe
Add a static image (or a different image for every orientation / Slide-Over width) to your asset catalogue.
Keep a pointer to an UIImageView that will be used as an overlay
UIImageView *_snapshotCover;
In your application delegate
- (void)applicationWillResignActive:(UIApplication *)application
{
// 1. build or reuse the overlay
if (!_snapshotCover) {
UIImage *img = [self _snapshotImageForCurrentTraitCollection];
_snapshotCover = [[UIImageView alloc] initWithImage:img];
_snapshotCover.contentMode = UIViewContentModeScaleAspectFill;
_snapshotCover.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
}
_snapshotCover.frame = self.window.bounds;
[self.window addSubview:_snapshotCover];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[_snapshotCover removeFromSuperview];
// the UIImageView is kept in memory so it can be reused next time
}
(Optional) If you want the picture to follow size-class changes while
you are still active (Slide-Over) adjust the image in
viewWillTransitionToSize: or in traitCollectionDidChange:.
Nothing in the code above talks to the GL context, so it fully respects
Apple’s rule about GPU calls after applicationDidEnterBackground:. At
the same time the system has crisp UIKit content to snapshot for every
orientation or size class it asks for, which means:
• A good looking thumbnail in the iOS App-Switcher
• No ugly scaled picture when the user launches the app again
• Still 100 % compliant with the multitasking rules for OpenGL ES.
Launch-image fallback
If you do nothing special the system will try to fall back to the launch screen storyboard / launch images when it cannot obtain a fresh snapshot. Therefore, as a minimal fix, make sure that your launch resources exist for every orientation you intend to support. Using the overlay technique above gives you full control and a better result, but supplying the correct launch images is enough to avoid the grotesquely scaled picture you described.
Summary
applicationWillResignActive:; remove it in
applicationDidBecomeActive:.Was this answer helpful?
version: o3-pro-2025-06-10
Status: UQ Validated
Validated: 8 months ago
Status: Needs Human Verification
Loading model reviews...
Loading reviews...