I can get GameScene:SKScene to appear using the layout constraints, but why can I still draw inside the safe ares?

Below is the code from GameViewController I use to present my GameScene. GameScene comes out respecting the safe areas (iPhone X).

However if I create an SKSprite node, the coords for the upper screen draw into the safe area.

My impression was that would not happen, nor can I find the top and bottom anchors.

p.s. on a side note, I cannot view originalS.frame.width or originalS.frame.height in the debugger, they come out as invalid expressions. yet they don't crash the app.
Code Block
class GameViewController: UIViewController {
private let myView : UIView = {
let myView = UIView()
myView.translatesAutoresizingMaskIntoConstraints = false
myView.backgroundColor = .clear
return myView
}()
private func addConstraints(){
var constraints = [NSLayoutConstraint]()
constraints.append(myView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor))
constraints.append(myView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor))
constraints.append(myView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor))
constraints.append(myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor))
NSLayoutConstraint.activate(constraints)
}
let logo = UIImage(named: "startup")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myView)
addConstraints()
if let view = self.view as! SKView?
{
myGlobalVars.widthPixels = UIScreen.main.nativeBounds.width
myGlobalVars.heightPixels = UIScreen.main.nativeBounds.height
myGlobalVars.widthPoints = UIScreen.main.bounds.width
myGlobalVars.heightPoints = UIScreen.main.bounds.height
originalS = myView
originalS.backgroundColor = .clear
originalS.translatesAutoresizingMaskIntoConstraints = false
originalS.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
originalS.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
originalS.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
originalS.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true
var scene : GameScene!
DispatchQueue.main.async {
scene = GameScene(size: CGSize(width: originalS.frame.width,
height: originalS.frame.height))
scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
scene.backgroundColor = .black
scene.scaleMode = .aspectFit
myGlobalVars.sceneRect = scene.frame
myGlobalVars.gameScene = scene
view.isHidden = false
view.presentScene(scene)
}
myGlobalVars.passGo = true
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = false
}
}
In your code, the host view presenting the scene is view, not myView.
Try creating myView as an instance of SKView and making it present the scene.
Ok, I went back and forth on OOper's advice, and after repeated errors I finally came across the right combination, by accident.

GameViewController launches the scene GameScene, which respects the constraints/safeAreaLayouts.

However, it's a mish-mash of using myView and view. While it works, am not sure why, because reading it kind of seems disjointed. I get the constraints from view, but launch from the sub-view myView. Am I getting lucky? Or is this bound to crash?

Code Block
import UIKit
import SpriteKit
import GameplayKit
var myGlobalVars = GlobalVars(backGround: SKSpriteNode(),
pegHolder: SKSpriteNode(), currentNode: SKSpriteNode(),
widthPoints: 0.0, heightPoints: 0.0,
widthPixels: 0.0, heightPixels: 0.0, passGo: false, sceneRect: .zero, fileName: " ")
class GameViewController: UIViewController {
private let myView : UIView = {
let myView = UIView()
myView.translatesAutoresizingMaskIntoConstraints = false
myView.backgroundColor = .clear
return myView
}()
private func addConstraints(){
var constraints = [NSLayoutConstraint]()
constraints.append(myView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor))
constraints.append(myView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor))
constraints.append(myView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor))
constraints.append(myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor))
NSLayoutConstraint.activate(constraints)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = view.safeAreaInsets.top
bottomSafeArea = view.safeAreaInsets.bottom
} else {
topSafeArea = topLayoutGuide.length
bottomSafeArea = bottomLayoutGuide.length
}
}
let logo = UIImage(named: "startup")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myView)
addConstraints()
if let view = self.view as! SKView?
{
myGlobalVars.widthPixels = UIScreen.main.nativeBounds.width
myGlobalVars.heightPixels = UIScreen.main.nativeBounds.height
myGlobalVars.widthPoints = UIScreen.main.bounds.width
myGlobalVars.heightPoints = UIScreen.main.bounds.height
var scene : GameScene!
DispatchQueue.main.async { [self] in
scene = GameScene(size: CGSize(width: myView.frame.width,
height: myView.frame.height))
scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
scene.backgroundColor = .clear
scene.scaleMode = .aspectFit
myGlobalVars.sceneRect = scene.frame
myGlobalVars.gameScene = scene
view.isHidden = false
view.presentScene(scene)
}
myGlobalVars.passGo = true
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return false
}
}

While it works, am not sure why

Neither am I. Frankly, I cannot understand what is the significant difference between your previous code and the new code.

Am I getting lucky? Or is this bound to crash?

Hope some experts might find out...


Your myView is still a UIView, not SKView. Your view is still an SKView, not UIView.

This is the code of GameViewController.swift in a simplified project for testing what I suggested.
(Constraints are added on the Interface Builder.)
Code Block
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
@IBOutlet weak var myView: SKView! //<-
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.myView { //<-
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return true
}
}


Not sure if I mentioned it in my OP, but am hardcoding everything. Not using storyboard at all.

Not using storyboard at all.

Whether you are using storyboard or not is not important here. You can instantiate an SKView and add constraints to it manually.


The problem is:

Your myView is still a UIView, not SKView. Your view is still an SKView, not UIView.

My code shown is just an example that what I suggested do work.
I can get GameScene:SKScene to appear using the layout constraints, but why can I still draw inside the safe ares?
 
 
Q