0

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?

Farquad
  • 3
  • 4
  • Try replacing `self.titleLabel?.textColor = textColor` with `self.setTitleColor(textColor, for: .normal)`. Probably best not to use two completely different approaches to setting up the colors. – HangarRash Mar 13 '23 at 04:07
  • Thanks for the suggestion! I made that change for better consistency but it did not fix the issue with the text color in the .disabled state – Farquad Mar 13 '23 at 18:43

2 Answers2

0

self.setTitleColor(color, for: .disabled) is wrong. Please use self.setTitleColor(textColor, for: .disabled)

When the button enters the disabled state, an alpha value is applied to both the text and background. If you want the text color to remain solid without alpha, set the text color using the .disabled state. Otherwise, use the .normal state for text color.

Divagar.G
  • 41
  • 4
  • Thank you, good catch! I was using the wrong color variable and fixed the error. However it did not fix issue with the text color in the .disabled state – Farquad Mar 13 '23 at 18:48
0

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:

  • The top button - "button A" - will use the default color values
  • we'll set custom color values for the bottom button - "button B"
  • tapping the top button will toggle .isEnabled state of the bottom button

Controller 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:

enter image description here

while the bottom button is tapped (highlighted):

enter image description here

and after tapping the top button to disable the bottom button:

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thanks for the detailed response! This is definitely a better way to organize the function. I changed my code using this as a reference but I am still getting the same issue. I created the button using storyboard and set its class to CustomButton inside the Attribute Inspector. Could some storyboard attribute be overwriting or superseding the titleColor we set programmatically? – Farquad Mar 13 '23 at 19:38
  • Interestingly enough, I am now noticing that the background color also does not seem to be affected by anything I set programmatically. It seems likely that something is going on with how I set it up in storyboard – Farquad Mar 13 '23 at 19:46
  • In Storyboard, are you changing the button **Style** from **Plain** to **Default**? If not, do so. – DonMag Mar 13 '23 at 19:59