Reputation: 83
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
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:
Storyboard
(leave the default color), but assign it directly in code. In this case, you can assign a color, including in viewDidLoad
DispatchQueue.main.async {}
and still use it in viewDidLoad
viewDidAppear
, but in this case, the view may blink. Moving the code to viewWillAppear
doesn't solve the problem!Upvotes: 0
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
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