Using iOS 11 safe area in a SpriteKit game

I have a game built on SpriteKit that I am trying to update for compatibility with the iPhone X. Mainly, I have to make sure my interactive game elements are inside the safe area introduced in iOS 11. However, there is a timing issue with accessing the safe area during my game initialization, and my workaround is not pretty.


Here is how my game used to launch, following the model suggested by Apple:


(1) iOS displays my launch screen.

(2) SKScene.didMove(to view) is called, and my code creates game content based on the screen size.

(3) iOS displays my view by fading from the launch screen to the game content.


The issue is that when SKScene.didMove is called, the view is not displayed yet, and therefore the safe area information (view.safeAreaInsets) is not initialized yet. This is working as expected according to Apple's documentation. To work around this, I have come up with this new launch procedure:


(1) iOS displays my launch screen.

(2) SKScene.didMove(to view) is called, and my code creates a replica of the launch screen.

(3) iOS displays my view by fading from the launch screen to the replica.

(4) viewDidAppear is called on my ViewController. At this point the safe area is set correctly (view.safeAreaInsets is set correctly), and my code creates game content based on the screen size and safe area.

(5) In order to prevent the game content from suddenly popping on screen, my code initially sets the game content alpha to zero, and then fades it in.


This procedure is obviously more convoluted than the original. For some games replicating the launch screen (2) might be a pain point, but in my game the background is already identical to the launch screen, so it's not a problem. The pain point for me is fading the content (5). I implemented this by creating a rasterized SKEffectNode that wraps all the game content, and then running a fade action on that. However rasterizing all the sprites seems to take some time, and the animation does not run smoothly (content just pops in) unless I add a significant delay before the animation (at least 300 ms on hardware I tried). So in addition to having more complicated code, I also have a measurably slower launch.


I'd love to know a better way to handle this. If I could access the safe area insets before my view is displayed, that would solve my issues.

Replies

If you present your scene from within viewWillLayoutSubviews(), the safeAreaInsets will be set, so you can safely layout your scene according to the Safe Area as soon as didMove(to: SKView) is called.