I have a form to collect basic user info. There is a "Continue" button on the form that has different appearances based on whether it is in the .enabled
or .disabled
state.
The .enabled
state formatting works fine with the setTitleColor
method. But when I execute setTitleColor
for the .disabled
state, the button text color changes to a different color than the one I want. What is going on?
I have a CustomButton
subclass of UIButton
that changes its state to .disabled
when the text fields above it are empty. Then I call a function to do some custom formatting to give the .disabled
button a medium grey background with a light grey text.
class CustomButton: UIButton {
// Format a button with filled background
// Default button text color to white if no parameter is provided
func configFilledButton(color: UIColor, textColor: UIColor? = UIColor.white) {
self.titleLabel?.textColor = textColor
self.backgroundColor = color
self.setTitleColor(color, for: .disabled)
} // close configFilledButton
// Call this function to toggle the Continue button state to disabled
func toggleEnabledState(isEnabled: Bool) {
if isEnabled == false {
let newColor = UIColor(named: K.BrandColors.greyMedium)!
self.configFilledButton(
color: newColor,
textColor: UIColor(named: K.BrandColors.greyLight))
} // close toggleEnabledState
} // close CustomButton
When I run the code to disable the button, the button background changes to the correct color but the title text changes to a color I did not set (I assume it is using some default color for disabled buttons). I verified that the colors I am using in my Assets folder are correctly named and referenced in my constants K.brandColors
structure.
Any idea why setTitleColor
does not seem to be working for the .disabled
state?
You can save yourself a lot of work by making use of the .normal
/ .highlighted
/ .disabled
states... and then overriding isEnabled
to handle the background color change:
class CustomButton: UIButton {
// these will be set by configure()
private var bkgEnabled: UIColor = .white
private var bkgDisabled: UIColor = .black
override init(frame: CGRect) {
super.init(frame: frame)
// configure with defaults
self.configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// configure with defaults
self.configure()
}
// Format a button...
// Each parameter has a "default" value
func configure(titleNormal: UIColor? = .white,
titleHighlighted: UIColor? = .lightGray,
titleDisabled: UIColor? = .darkGray,
bkgEnabled: UIColor? = .red,
bkgDisabled: UIColor? = .lightGray) {
self.setTitleColor(titleNormal, for: .normal)
self.setTitleColor(titleHighlighted, for: .highlighted)
self.setTitleColor(titleDisabled, for: .disabled)
self.backgroundColor = self.isEnabled ? bkgEnabled : bkgDisabled
// update background color properties
self.bkgEnabled = bkgEnabled!
self.bkgDisabled = bkgDisabled!
}
// this will set the background color, based on Enabled / Disabled state
override var isEnabled: Bool {
didSet {
self.backgroundColor = isEnabled ? bkgEnabled : bkgDisabled
}
}
}
Here's an example controller... it will create two custom buttons:
.isEnabled
state of the bottom buttonController class:
class ViewController: UIViewController {
let customButtonA = CustomButton()
let customButtonB = CustomButton()
override func viewDidLoad() {
super.viewDidLoad()
customButtonA.translatesAutoresizingMaskIntoConstraints = false
customButtonB.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(customButtonA)
view.addSubview(customButtonB)
NSLayoutConstraint.activate([
customButtonA.centerXAnchor.constraint(equalTo: view.centerXAnchor),
customButtonA.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -80.0),
customButtonB.centerXAnchor.constraint(equalTo: view.centerXAnchor),
customButtonB.topAnchor.constraint(equalTo: customButtonA.bottomAnchor, constant: 40.0),
])
customButtonA.setTitle("Toggle enabled/disabled", for: [])
customButtonB.setTitle("Enabled", for: [])
customButtonA.addTarget(self, action: #selector(btnATapped(_:)), for: .touchUpInside)
customButtonB.addTarget(self, action: #selector(btnBTapped(_:)), for: .touchUpInside)
customButtonB.configure(titleNormal: .red, titleHighlighted: .green, titleDisabled: .black, bkgEnabled: .yellow, bkgDisabled: .gray)
}
@objc func btnATapped(_ sender: UIButton) {
print("Toggling .isEnabled of button B")
customButtonB.isEnabled.toggle()
let t: String = customButtonA.isEnabled ? "Enabled" : "Disabled"
customButtonB.setTitle(t, for: [])
}
@objc func btnBTapped(_ sender: UIButton) {
print("Button B tapped")
}
}
class CustomButton: UIButton {
// these will be set by configure()
private var bkgEnabled: UIColor = .white
private var bkgDisabled: UIColor = .black
override init(frame: CGRect) {
super.init(frame: frame)
// configure with defaults
self.configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// configure with defaults
self.configure()
}
// Format a button...
// Each parameter has a "default" value
func configure(titleNormal: UIColor? = .white,
titleHighlighted: UIColor? = .lightGray,
titleDisabled: UIColor? = .darkGray,
bkgEnabled: UIColor? = .red,
bkgDisabled: UIColor? = .lightGray) {
self.setTitleColor(titleNormal, for: .normal)
self.setTitleColor(titleHighlighted, for: .highlighted)
self.setTitleColor(titleDisabled, for: .disabled)
self.backgroundColor = self.isEnabled ? bkgEnabled : bkgDisabled
// update background color properties
self.bkgEnabled = bkgEnabled!
self.bkgDisabled = bkgDisabled!
}
// this will set the background color, based on Enabled / Disabled state
override var isEnabled: Bool {
didSet {
self.backgroundColor = isEnabled ? bkgEnabled : bkgDisabled
}
}
}
Looks like this to start:
while the bottom button is tapped (highlighted):
and after tapping the top button to disable the bottom button: