How to get safe area insets from within a SKScene subclass

168 Views Asked by At

I'm subclassing SKScene to create a main menu scene and would like to add a button at the top left of the screen. Since the status bar is enabled I would want buttons to be placed below the status bar. To do this, I need the safe area insets, but every time I try to access the safe area insets I get the value of 0.0 for each edge.

According to the documentation, this happens when the view hasn't appeared on screen, however, I can't figure out a way to access the safe area insets after the view has appeared. sceneDidLoad(), didMove(to:), etc. all seem to be called before viewDidAppear() so I'm not able to acquire the safe area insets from within those functions.

Can anyone tell me how I can place my SKSpriteNodes (menu buttons) within the safe area by using safeAreaInsets? Keep in mind I need to access it within the SKScene subclass so I can place my menu button properly.

Thanks in advance.

1

There are 1 best solutions below

2
Fault On

formerly it was easy to access insets from within SKScene

UIApplication.shared.windows.first?.safeAreaInsets

'windows' was deprecated in iOS 15.0. however you can still access insets like this

self.view?.window?.windowScene?.windows.first?.safeAreaInsets

but, as you say, this doesn't work within the launch cycle of SKScene. you have to access it afterward.

as a solution i recommend the following pattern: centralize all your SKNode positioning into a function called rebuild. at the start of that rebuild function also update safeAreaInsets. then call rebuild from UIViewController.viewDidAppear

SKScene:

var topInset:CGFloat = 0

func pollInsets() {
     topInset = self.view?.window?.windowScene?.windows.first?.safeAreaInsets.top ?? 0
     print("topInset: \(topInset)")
}

func rebuild() {
     pollInsets()
     
     //reposition your buttons, etc.
}

UIViewController:

override func viewDidAppear(_ animated: Bool) {
     if let view = self.view as! SKView? {
          (view.scene as? GameScene)?.rebuild()
     }
}