How do I set UIButton background color forState: UIControlState.Highlighted in Swift

IosSwiftUibuttonXcode6

Ios Problem Overview


I can set the background color for a button but I can't work out how to set the background color for UIControlState.Highlighted. Is it even possible? or do I need to go down the setBackgroundImage path?

Ios Solutions


Solution 1 - Ios

If anyone stops by, another way to go maybe more easily if it is something you need more than once... I wrote a short extension for UIButton, it works just fine:

for Swift 3

extension UIButton {
    func setBackgroundColor(color: UIColor, forState: UIControlState) {
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), color.CGColor)
        CGContextFillRect(UIGraphicsGetCurrentContext(), CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        self.setBackgroundImage(colorImage, forState: forState)
    }
}

for Swift 4

extension UIButton {
    func setBackgroundColor(color: UIColor, forState: UIControl.State) {
        self.clipsToBounds = true  // add this to maintain corner radius
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        if let context = UIGraphicsGetCurrentContext() {
            context.setFillColor(color.cgColor)
            context.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
            let colorImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            self.setBackgroundImage(colorImage, for: forState)
        }
    }
}

You use it just like setBackgroundImage:

yourButton.setBackgroundColor(color: UIColor.white, forState: UIControl.State.highlighted)

Solution 2 - Ios

Syntax changes to @winterized extension for Swift 3+ syntax

extension UIButton {
    func setBackgroundColor(color: UIColor, forState: UIControlState) {
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
        UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.setBackgroundImage(colorImage, for: forState)
    }}

Solution 3 - Ios

Below will be one way to go. Two IBActions. One to control background color when depressing a button, one on release.

import UIKit

class ViewController: UIViewController {
    
    
    @IBOutlet weak var button: UIButton!

    @IBAction func buttonClicked(sender: AnyObject) { //Touch Up Inside action
        button.backgroundColor = UIColor.whiteColor()
    }
    
    @IBAction func buttonReleased(sender: AnyObject) { //Touch Down action
        button.backgroundColor = UIColor.blueColor()

    }
  
    override func viewDidLoad() {
        super.viewDidLoad()

    }
}

When you look at the autocomplete options for your button after adding a period, you can set a background color, but not for specified state. You can only set background images. Now of course if you are married to doing it this way instead of using the method I show above, you could load an image of the desired color as the background image using the setbackgroundImageForState property.

enter image description here

enter image description here

Solution 4 - Ios

Swift 4+ compatibility for the accepted answer :

extension UIButton {

  /// Sets the background color to use for the specified button state.
  func setBackgroundColor(color: UIColor, forState: UIControlState) {
    
    let minimumSize: CGSize = CGSize(width: 1.0, height: 1.0)

    UIGraphicsBeginImageContext(minimumSize)

    if let context = UIGraphicsGetCurrentContext() {
      context.setFillColor(color.cgColor)
      context.fill(CGRect(origin: .zero, size: minimumSize))
    }

    let colorImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    self.clipsToBounds = true
    self.setBackgroundImage(colorImage, for: forState)
  }
}

Compatible SwiftLint and fix the bug of broken auto layout / corner radius.

Solution 5 - Ios

Swift 4 Version of this solution:

extension UIButton {

    func setBackgroundColor(_ color: UIColor, for state: UIControlState) {
        
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
        UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        setBackgroundImage(colorImage, for: state)
    }
}

Solution 6 - Ios

Seems nobody here has mentioned using Key Value Observation yet, but it's another approach.

A reason for doing so instead of picking the other answers here is you don't need to go creating new images all the time nor be concerned with secondary effects of assigning images to buttons (e.g. cornerRadius effects).

But you'll need to create a class for the observer, who would be responsible for storing the different background colours and applying them in the observeValue() method.

public class ButtonHighlighterObserver: NSObject {

  var observedButton:UIButton? = nil

  var backgroundColor: UIColor            = UIColor.white
  var backgroundHighlightColor: UIColor   = UIColor.gray

  public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    
    // Perform background color changes when highlight state change is observed
    if keyPath == "highlighted", object as? UIButton === observedButton {
        observedButton!.backgroundColor = observedButton!.isHighlighted ? self.backgroundHighlightColor : self.backgroundColor
    }
}

}

Then all you need to do is manage addObserver / removeObserver during operation:

// Add observer to button's highlighted value
button.addObserver(anObserver, forKeyPath: "highlighted", options: [.new], context: nil)
anObserver.observedButton = button

// ...

// And at deinit time, be sure you remove the observer again
anObserver.observedButton?.removeObserver(item, forKeyPath: "highlighted")
anObserver.observedButton = nil

Solution 7 - Ios

Update Swift 4

 extension UIButton {
    
    func setBackgroundColor(color: UIColor, forState: UIControlState) {
        
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
        UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
      
        self.setBackgroundImage(colorImage, for: forState)
    }

Event Button Action

Show Touch On Hightlight

Solution 8 - Ios

Why not?

@implementation UIButton (Color)

- (void) setBackgroundColor:(UIColor*)color forState:(UIControlState)state
{
    [self setBackgroundImage:[UIImage.alloc initWithCIImage:[CIImage imageWithColor:[CIColor colorWithCGColor:color.CGColor]]] forState:state];
}

@end

Solution 9 - Ios

in Swift 5

For those who don't want to use colored background to beat the selected state

Simply you can beat the problem by using #Selector & if statement to change the UIButton colors for each state individually easily

For Example:

	override func viewDidLoad() {
	super.viewDidLoad()
    //to reset the button color to its original color ( optionally )
	self.myButtonOutlet.backgroundColor = UIColor.white  
}

@IBOutlet weak var myButtonOutlet: UIButton!{
	didSet{  // Button selector and image here
		self.myButtonOutlet.setImage(UIImage(systemName: ""), for: UIControl.State.normal)
		
		self.myButtonOutlet.setImage(UIImage(systemName: "checkmark"), for: UIControl.State.selected)
				


		self.myButtonOutlet.addTarget(self, action: #selector(tappedButton), for: UIControl.Event.touchUpInside)
	}
}

@objc func tappedButton() {  // Colors selection is here
	if self.myButtonOutlet.isSelected == true {
		
		self.myButtonOutlet.isSelected = false
		self.myButtonOutlet.backgroundColor = UIColor.white			
	} else {
		self.myButtonOutlet.isSelected = true

        self.myButtonOutlet.backgroundColor = UIColor.black
		self.myButtonOutlet.tintColor00 = UIColor.white

	}
}

Solution 10 - Ios

You can override isHighlighted and changed the background color when the isHighlighted is set.

Example: TextButton.Swift

import UIKit

class TextButton: UIButton {

   private var text: String = "Submit" {
       didSet{
           setText()
       }
   }

   var hightlightedColor : UIColor = UIColor(red: 50/255, green: 50/255, blue: 50/255, alpha: 1)

   var background :UIColor = .black {
       didSet{
           self.backgroundColor = background
       }
   }

   override var isHighlighted: Bool {
       didSet {
           self.backgroundColor = self.isHighlighted ? hightlightedColor : background
       }
   }

   // MARK: - Lifecycle
   override init(frame: CGRect) {
       super.init(frame: frame)
       sharedLayout()
   }

   required init?(coder aDecoder: NSCoder) {
       super.init(coder: aDecoder)
       sharedLayout()
   }



   // MARK: - Method
   private func setText() {
       self.setTitle(text, for: .normal)
   }

   private func sharedLayout() {
       self.setTitle(text, for: .normal)
       self.backgroundColor = self.isHighlighted ? .green : background
       self.layer.cornerRadius = 8
   }

}

Usages:

let nextBtn = TextButton()

Solution 11 - Ios

If using storyboard or xib use @IBInspectable by adding extension for UIButton

import Foundation
import UIKit
import ObjectiveC

// Declare a global var to produce a unique address as the assoc object handle
var highlightedColorHandle: UInt8 = 0

extension UIButton {

func setBackgroundColor(_ color: UIColor, for state: UIControl.State) {
    self.setBackgroundImage(UIImage.init(color: color), for: state)
}

@IBInspectable
var highlightedBackground: UIColor? {
    get {
        if let color = objc_getAssociatedObject(self, &highlightedColorHandle) as? UIColor {
            return color
        }
        return nil
    }
    set {
        if let color = newValue {
            self.setBackgroundColor(color, for: .highlighted)
            objc_setAssociatedObject(self, &highlightedColorHandle, color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        } else {
            self.setBackgroundImage(nil, for: .highlighted)
            objc_setAssociatedObject(self, &highlightedColorHandle, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

}

Use in Storyboard or Xib like

enter image description here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid WoodView Question on Stackoverflow
Solution 1 - IoswinterizedView Answer on Stackoverflow
Solution 2 - IosMaverickView Answer on Stackoverflow
Solution 3 - IosSteve RosenbergView Answer on Stackoverflow
Solution 4 - IosMaximelcView Answer on Stackoverflow
Solution 5 - IosdanielgehrView Answer on Stackoverflow
Solution 6 - IosMeteView Answer on Stackoverflow
Solution 7 - IosPuji WahonoView Answer on Stackoverflow
Solution 8 - IospoGUIstView Answer on Stackoverflow
Solution 9 - IosLet.SimooView Answer on Stackoverflow
Solution 10 - IosSwee KwangView Answer on Stackoverflow
Solution 11 - IosmustaqView Answer on Stackoverflow