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
Code Block
//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
Code Block
//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
Code Block
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.
Code Block
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
}

Answered by SergioDCQ in 644036022

Then you should better go back to your old thread:
I can get GameScene:SKScene to appear using the layout constraints, but why can I still draw inside the safe ares?

You have gone for a wrong direction too far, that's why many of your codes do not work as expected.

I have gone back to that, Googled some more, and found how to get the safeAreaLayout for the GameViewControler itself, code below, so I can skip the myView part, . But that itself brings up two issues...

Should I start another thread?
Code Block
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
#if DEBUG
print ("GVC viewWillLayoutSubviews")
#endif
let safeAreaInsets = self.view.safeAreaInsets;
}

Please clarify when and where you call createConstraints(view:) in your using-function code.
Line 34 - addConstraints() which shoots over to
Line 12
Sorry, not to be clear enough.

I want to know when and where you are calling the function createConstraints(view:).
Obviously I am not. The function addConstraints contains what I learned online on how to apply the safeAreaLayouts programmatically. Which does work, just not if I try and put it into a separate function.

So please, if you say I am doing this incorrectly to begin with, I am all ears. Am still new to this language.

if you say I am doing this incorrectly to begin with

Please show your whole code including the method you are calling createConstraints(view:). I want to see the code to examine in detail to check if you are doing incorrectly or not.
That is the whole code, at the very bottom of the OP. GameScene itself is empty currently.

The only two outside functions I call are:

Code Block
func getScreenDimensions (screen : inout ScreenDimensions)
{
screen.widthPix = UIScreen.main.nativeBounds.width
screen.heightPix = UIScreen.main.nativeBounds.height
screen.widthPts = UIScreen.main.bounds.width
screen.heightPts = UIScreen.main.bounds.height
}
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
}


Which work
While the code in the OP works, I moved everything screen dimension related to be run in the viewDidLayoutSubviews (which is where it should be) and at that moment I was able to move this code:


Code Block
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)
}

into it's own function, that I could then successfully call. I'm going to assume that my problem was trying do all this before viewDidLayoutSubviews and that's why I couldn't move it to its own function? Although am shocked it worked before since I called it too early.

Going to wait for your thoughts before I close this thread.

I moved everything screen dimension related to be run in the viewDidLayoutSubviews

Can you clarify what is everything?

I want to make my code clearer, so I've moved the addConstraints function to a separate file

Is it a separate file ? It appears inside the viewController, just outside viewDidLoad

But when I run my app that way self.myView.frame.width & self.myView.frame.height are zero.

Where precisely do you test them ?
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

Code Block
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
}
}

Thanks for feedback.

But I think that shows there is a basic design flaw in this code (and maybe elsewhere): you make some initialisations in wrong places.

For instance, why do you initialize myGlobalVars in viewDidLayoutSubviews ?

Code Block
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
}
}


If you update, why not. But it should be initialised in viewDidLoad or viewWillAppear.

Why did it work before ? It may happen that the order of some API calls change (I've noticed this in other cases). And thus, accidentally, viewDidLayoutSubviews was called before in previous iOS versions.

every call regarding screen dimensions, and safeAreaLayout, are done in the viewDidLayoutSubviews function.

I strongly recommend not to add subview, not to change or add constraints in viewDidLayoutSubviews.

For instance, why do you initialize myGlobalVars in viewDidLayoutSubviews ?

Thank you for pointing that out. Claude31, Yes need to move that to viewDidLoad


I strongly recommend not to add subview, not to change or add constraints in viewDidLayoutSubviews.

Hello OOper, you've been super helpful before. May I ask why? Every video, or online posts say that when viewDidLayoutSubviews is called, that that's when you can be guaranteed that the scene's dimensions are set properly.

The reason I add a subView was because I couldn't call the constraints on GameViewController. If I am doing this wrong, would love to hear how I should be doing it.

when viewDidLayoutSubviews is called, that that's when you can be guaranteed that the scene's dimensions are set properly.

Dimensions yes, to be updated.
But not adding the subview. You risk ending creating some recursive infinite calls that will crash your app.

 May I ask why?

Please check  Claude31's reply carefully.

If I am doing this wrong, would love to hear how I should be doing it.

Then you should better go back to your old thread:
I can get GameScene:SKScene to appear using the layout constraints, but why can I still draw inside the safe ares?

You have gone for a wrong direction too far, that's why many of your codes do not work as expected.
Am having trouble moving code into a separate function
 
 
Q