swiftuiviewuikitsafearealayoutguide

Why does getting my view's frame return the full screen, even tho the view has been run through the safeArea constraints?


Before presenting my GameScene I apply the safeAreaLayoutGuide to the initial view screen (code below is from GameViewController).

If at any point before I call view.presentScene(scene) I try to get the view's frame, I get back an empty set.

If however I get the frame, after presenting the scene, I get the rect for the full screen, even though the view does appear within the safeAreaLayoutGuide.

How can I get the view's safe adjusted screen coordinates? I should note I am doing this so I can apply the proper constraints to my GameScene, as I can only find how to do it for UIView.

class GameViewController: UIViewController {
    override func viewDidLoad() {
    super.viewDidLoad()

    if let view = self.view as! SKView?{
        let screenWidthInPoints = UIScreen.main.bounds.width
        let screenHeightInPoints = UIScreen.main.bounds.height

        let imageView = UIView()
        imageView.backgroundColor = .red
        view.addSubview(imageView)

        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true



        let scene = GameScene(size: CGSize(width: screenWidthInPoints,
        height: screenHeightInPoints))
        scene.backgroundColor = .black
        scene.scaleMode = .aspectFit
        view.presentScene(scene)
        var myRect = view.frame

Solution

  • UIScreen.main.bounds represents the entire screen, not just the safe area. You will have to create a separate view constrained to safe area insets to get the proper safe area size. In your case, you already have an imageView placed within the safe area. Try printing out its size after adding the constraints and do it within a DispatchQueue.main.async to make sure that it gets called only after the current pass layout is complete.

        let imageView = UIView()
        imageView.backgroundColor = .red
        view.addSubview(imageView)
        
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true
        
        
        DispatchQueue.main.async {
            print("imageView.frame: ", imageView.frame)
            
            let scene = GameScene(size: CGSize(width: imageView.frame.width,
                                               height: imageView.frame.height))
            scene.backgroundColor = .black
            scene.scaleMode = .aspectFit
            view.presentScene(scene)
        }