StronkStinq
StronkStinq

Reputation: 83

swift setting properties in viewDidLoad doesn't work but works in viewDidAppear

Latest XCode. I have a project in which in any storyboards/view controller setting properties such as textColor of a UILabel or backgroundColor of a UIButton in viewDidLoad doesn't work but works in viewDidAppear.

What kills me is that I can't reproduce it in a new/different simple project.

Anyone has any idea what could possibly be wrong? It's a big project. Would be a major mess to recreate it. I have a feeling that this is unresolvable. Please prove me wrong.

Sorry not much code to show besides:

@IBOutlet weak var theTextField: UITextField!
@IBOutlet weak var theLabel: UILabel!
@IBOutlet weak var theButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()
    theTextField.text = "some text"      //<<<- works
    theTextField.textColor = UIColor.red //<<<- works
    theLabel.textColor = UIColor.red     //<<<- **doesn't work**
    theButton.backgroundColor = UIColor.red //<<<- **doesn't work**
    theButton.setTitleColor(UIColor.blue, for: UIControl.State.normal) //<<<- works

}
override func **viewDidAppear**(_ animated: Bool) {
    super.viewDidAppear(animated)
    theTextField.text = "some text"         //<<<- works
    theTextField.textColor = UIColor.red    //<<<- works
    theLabel.textColor = UIColor.red        //<<<- **works**
    theButton.backgroundColor = UIColor.red //<<<- **works**
    theButton.setTitleColor(UIColor.blue, for: UIControl.State.normal) //<<<- works
}

Here theTextField is updated fine in all cases, but a label and a button won't update in 'viewDidLoad'.

EDIT: updated code to show that some things work and other don't

EDIT 2: problem with using viewDidAppear is that the screen shows up with 1 set of colors (specified in designer) but then all colors switch in front of the user - which looks ugly. And any kind of delays won't help it. The goal is to "prepare" the screen with all colors and such before it is animated into view. And these colors depend on some dynamic data and can't be specified in the designer.

EDIT 3: I just figured that doing all of this in viewDidLayoutSubviews gets me what I want:

@IBOutlet weak var theTextField: UITextField!
@IBOutlet weak var theLabel: UILabel!
@IBOutlet weak var theButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()
    theTextField.text = "some text"      //<<<- works
    theTextField.textColor = UIColor.red //<<<- works
    theLabel.textColor = UIColor.red     //<<<- doesn't work
    theButton.backgroundColor = UIColor.red //<<<- doesn't work
    theButton.setTitleColor(UIColor.blue, for: UIControl.State.normal) //<<<- works

}
override func viewDidLayoutSubviews() {
    super.viewDidAppear()
    theTextField.text = "some text"         //<<<- works
    theTextField.textColor = UIColor.red    //<<<- works
    theLabel.textColor = UIColor.red        //<<<- works
    theButton.backgroundColor = UIColor.red //<<<- works
    theButton.setTitleColor(UIColor.blue, for: UIControl.State.normal) //<<<- works
}

Seems like a possible workaround. But why? Why do I need to resort to a workaround? I really would like to understand why viewDidLoad doesn't work in my this particular case/project. Or is this not a workaround and a proper way to do this? Everywhere I've seen in all sample code everybody always does all this sort of initialization in viewDidLoad. I've never seen anyone doing it in viewDidLayoutSubviews or viewDidAppear. Have I not looked long enough? What am I missing?

Upvotes: 4

Views: 730

Answers (4)

Vergiliy
Vergiliy

Reputation: 1356

I also see this issue in iOS 12. The error only occurs when you assign a color through the Storyboard. If you use the default color on the Storyboard, then everything works as expected. This is an iOS bug that was fixed in version 13.

I found several ways to solve this problem:

  1. The best option! Do not change the default color on Storyboard (leave the default color), but assign it directly in code. In this case, you can assign a color, including in viewDidLoad
  2. You can wrap the code in DispatchQueue.main.async {} and still use it in viewDidLoad
  3. You can transfer the code to viewDidAppear, but in this case, the view may blink. Moving the code to viewWillAppear doesn't solve the problem!

Upvotes: 0

gnasher729
gnasher729

Reputation: 52530

viewWillAppear is a better place than viewDidAppear.

viewDidLoad should work. Did you set a breakpoint and check that your code is actually called? It wouldn't be called if a subclass doesn't call super.viewDidLoad().

And did you check that theLabel and theButton are not nil? Did you check your code to make sure theLabel and theButton are not set elsewhere?

Upvotes: 0

llkenny
llkenny

Reputation: 146

Setting label color to Default solved same problem for me.

enter image description here

Upvotes: 0

Manish_Nainwal
Manish_Nainwal

Reputation: 301

Nothing is wrong. It rendered the label before executing the code. Try the below code it'll work

@IBOutlet weak var theLabel: UILabel!
override func viewDidLoad() {
    super.viewDidLoad()
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
        theLabel.textColor = UIColor.red
    })
}

Upvotes: 0

Related Questions