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?

- 3,324
- 3
- 19
- 21

- 1,319
- 2
- 12
- 15
-
Check this post https://somethingaboutios.wordpress.com/2016/02/09/uibutton-backgroundcolor-for-uicontrolstateselected/ Swift example at the end of the post. – Gabriel.Massana Feb 09 '16 at 21:03
12 Answers
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)

- 777
- 6
- 20

- 3,324
- 3
- 19
- 21
-
18
-
@stephan1001 how will it do that? The image is only 1x1 so it's stretched to fit the button? – Erik Rothoff Jun 21 '15 at 15:50
-
1@ user4788711 just as setting a background image would. Not more, not less. – winterized Jun 23 '15 at 15:42
-
@ErikPerik Exactly. As it is a plain color and not an actual image, it is stretched flawlessly :) – winterized Jun 23 '15 at 15:44
-
13in order to fix corner radius you just need to enable Clip subviews on button – heximal Sep 12 '15 at 20:58
-
Works great. Thank you. Also, for anyone new to extensions (like me), I had an error at first. I had to paste the extension code outside of the class. After that, it worked wonderfully. – Dave G Jan 02 '16 at 22:44
-
2For me, it was totally worth it to extend UIImage with an initializer for the color part. Then this function's body becomes simply `self.setBackgroundImage(UIImage(color: color), forState: forState)` – Phlippie Bosman Sep 05 '17 at 09:44
-
to keep the functionality of the cornerRadius, just ensure that `layer.masksToBounds = true` – T. Hyldgaard Jan 24 '18 at 13:43
-
this code destoys title of the button. i mean that image becomes above the title. and title not shown – swift2geek Apr 29 '18 at 14:14
-
1Why the need to use a graphicContext? ```button.setBackgroundImage(UIImage(color: .red), for: .normal)``` works in one line – Marco Pappalardo Jul 31 '18 at 08:54
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)
}}

- 3,209
- 1
- 34
- 40
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.

- 19,348
- 7
- 46
- 53
-
2That solution works only in a ViewController but not in a TableViewController. The touched down action works also in TVC but only with some miliseconds delay, sothat a fast touch on screen won't change color of button. I think there's somewhere a problem with the cell in which the button is, but i'm not sure. – S. Birklin Jun 11 '15 at 11:44
-
I ran exactly in the same problem. If I only tap the view than nothing happens because the color is already back to the old color. I could fix this problem with a little delay so that the color is switched back some milliseconds later. So the color is visible even if the user only taps shortly. – Philipp Otto Aug 19 '15 at 15:49
-
1Also, I think the "Touch Down" method is risky. First, it doesn't reset the color automatically. That's easy enough, on "Touch-up Inside" just set it back to the original color. But what if the user does something abnormal, like pushes down, then decides not to continue, and drags their finger off. The color won't reset. – Dave G Jan 02 '16 at 22:56
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.

- 2,384
- 1
- 21
- 17
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)
}
}

- 127
- 1
- 6
-
4Just a small improvement to the code, to avoid force-unwraps try doing this: `if let currentGraphicsContext = UIGraphicsGetCurrentContext() { currentGraphicsContext.setFillColor(color.cgColor) currentGraphicsContext.fill(CGRect(x: 0, y: 0, width: 1, height: 1)) }` Plus adding `layer.masksToBounds = true` to keep the functionality of `layer.cornerRadius` – T. Hyldgaard Jan 24 '18 at 13:41
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

- 5,495
- 4
- 32
- 40
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

- 338
- 5
- 10
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

- 299
- 4
- 10
-
1I tried this, but i had to crop the image, for example to width: 1, height: 1 to make it work. With the infinite extent resulting from imageWithColor it didn't work. – Felix Lieb Aug 07 '19 at 12:33
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()

- 724
- 9
- 15
Swift 5
In my case I needed something else, because the option with the extension and setBackgroundColor function doesn't work properly in case when we have different background button colors for dark/light mode and when we change traitCollection.userInterfaceStyle (dark/light mode). So I used custom implementation of UIButton
class and override properties isHighlighted
and isEnabled
:
import UIKit
class CustomButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.isEnabled = isEnabled
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var isHighlighted: Bool {
didSet {
self.backgroundColor = isHighlighted ? UIColor.blue : UIColor.cyan
}
}
override var isEnabled: Bool {
didSet {
self.backgroundColor = isEnabled ? UIColor.cyan : UIColor.red
}
}
}

- 21
- 2
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
}
}

- 93
- 9
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

- 961
- 14
- 16