Search code examples
swiftuikitsafearealayoutguide

Am having trouble moving code into a separate function


From within my GameViewController's viewDidLoad() function I do two things:

1- I create a sub UIVIew, myView, which is added to the main view,

2- I call my function, addConstraints, which is in the GameViewController's code.

When I create GameScene, self.myView.frame.width & self.myView.frame.height contain the correct values, and abide by the safeAreaLayout guides.

I want to make my code clearer, so I've moved the addConstraints function to a separate file. But when I run my app that way self.myView.frame.width & self.myView.frame.height are zero.

So am curious if I am somehow translating the function incorrectly, or maybe this is something I can't move outside of the main code?

The first block is the original code, the 2nd is the function

//located with GameViewControl    
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)
    }

translated into a function

//called from GameViewControl
    createConstraints(view: myView)
    ....
        
//located outside of GameViewControl
func createConstraints(view: UIView) {
    var constraints = [NSLayoutConstraint]()
                    
    constraints.append(view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor))
    constraints.append(view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor))
    constraints.append(view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor))
    constraints.append(view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor))
                    
    NSLayoutConstraint.activate(constraints)
}

And here is the full file of GameViewControl

import UIKit
import SpriteKit

class GameViewController: UIViewController {
    
    private let myView : UIView = {
        let myView = UIView()
        setViewAttributes(view: myView)
        return myView
    }()

    private func addConstraints(){
         var constraints = [NSLayoutConstraint]()

        //add
         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))
        
        //activate
         NSLayoutConstraint.activate(constraints)
     }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        #if DEBUG
            print ("viewDidLoad")
        #endif
        
        if let view = self.view as! SKView? {
            getScreenDimensions (screen: &screenDims)
            view.addSubview(myView)
            addConstraints()

            var scene : GameScene!
            DispatchQueue.main.async { scene = GameScene(size: CGSize(width: self.myView.frame.width, height: self.myView.frame.width))
                scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
                scene.backgroundColor = .clear
                scene.scaleMode = .aspectFit
                view.isHidden = false
                view.presentScene(scene)
            }
            view.ignoresSiblingOrder    = true
            view.showsFPS               = true
            view.showsNodeCount         = true
            view.showsPhysics           = true
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        #if DEBUG
            print ("GVC viewDidLayoutSubviews")
        #endif

        myGlobalVars.sceneRect = view.frame
        
        if #available(iOS 11.0, *) {
            myGlobalVars.topSafeArea    = view.safeAreaInsets.top
            myGlobalVars.bottomSafeArea = view.safeAreaInsets.bottom
        } else {
            myGlobalVars.topSafeArea    = topLayoutGuide.length
            myGlobalVars.bottomSafeArea = bottomLayoutGuide.length
        }
    }
    
    override var shouldAutorotate: Bool {
        #if DEBUG
            print ("shouldAutorotate")
        #endif
        return false
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        #if DEBUG
            print ("supportedInterfaceOrientations")
        #endif
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }
    override var prefersStatusBarHidden: Bool {
        #if DEBUG
            print ("prefersStatusBarHidden")
        #endif
        return false
    }
}

The call to function setViewAttributes does pass the view to a function, and I have verified that that function is working.

func setViewAttributes(view: UIView)
{
    view.alpha                = 0.0
    view.frame.size.height    = UIScreen.main.nativeBounds.height
    view.frame.size.width     = UIScreen.main.nativeBounds.width
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor      = .clear
}

Solution

  • After going through the code, over and over I realized I was calling the safeAreaLayout routine before viewDidLayoutSubviews. Now I can move my call to a function and it works. Now I'd love to know why it was working before, when I was calling it prematurely. But maybe I should be grateful that I found a substantial error.

    So now my code looks the same as the code in the OP, except every call regarding screen dimensions, and safeAreaLayout, are done in the viewDidLayoutSubviews function.

    And addConstraints is now an external function

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        getScreenDimensions (screen: &screenDims)
        view.addSubview(myView)
        addConstraints()
        myGlobalVars.sceneRect = view.frame
        createConstraints(view: myView)
        
        if #available(iOS 11.0, *) {
            myGlobalVars.topSafeArea    = view.safeAreaInsets.top
            myGlobalVars.bottomSafeArea = view.safeAreaInsets.bottom
        } else {
            myGlobalVars.topSafeArea    = topLayoutGuide.length
            myGlobalVars.bottomSafeArea = bottomLayoutGuide.length
        }
    }