How to change the background color of a UIButton while it's highlighted?

| | August 7, 2015

At some point in my app I have a highlighted UIButton (for example when a user has his finger on the button) and I need to change the background color while the button is highlighted (so while the finger of the user is still on the button).

I tried the following:

_button.backgroundColor = [UIColor redColor];

But it is not working. The color remains the same. I tried the same piece of code when the button is not highlighted and it works fine. I also tried calling -setNeedsDisplay after changing the color, it didn’t have any effect.

How to force the button to change the background color? Thanks!

15 Responses to “How to change the background color of a UIButton while it's highlighted?”

  1. Try this !!!!

    For TouchedDown Event set One color and for TouchUpInside set the other.

    - (IBAction)touchedDown:(id)sender {
        NSLog(@"Touched Down");
        btn1.backgroundColor=[UIColor redColor];
    }
    
    - (IBAction)touchUpInside:(id)sender {
        NSLog(@"TouchUpInside");
        btn1.backgroundColor=[UIColor whiteColor];    
    }
    
  2. I have open-sourced a UIButton subclass, STAButton, to fill in this gaping functionality hole. Available under the MIT license. Works for iOS 7+ (I have not tested with older iOS versions).

  3. MattDiPasquale on November 30, -0001 @ 12:00 AM

    UPDATE:

    Use the UIButtonBackgroundColor Swift library.

    OLD:

    Use the helpers below to create a 1 px x 1 px image with a grayscale fill color:

    UIImage *image = ACUTilingImageGray(248/255.0, 1);
    

    or an RGB fill color:

    UIImage *image = ACUTilingImageRGB(253/255.0, 123/255.0, 43/255.0, 1);
    

    Then, use that image to set the button’s background image:

    [button setBackgroundImage:image forState:UIControlStateNormal];
    

    Helpers

    #pragma mark - Helpers
    
    UIImage *ACUTilingImageGray(CGFloat gray, CGFloat alpha)
    {
        return ACUTilingImage(alpha, ^(CGContextRef context) {
            CGContextSetGrayFillColor(context, gray, alpha);
        });
    }
    
    UIImage *ACUTilingImageRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)
    {
        return ACUTilingImage(alpha, ^(CGContextRef context) {
            CGContextSetRGBFillColor(context, red, green, blue, alpha);
        });
    }
    
    UIImage *ACUTilingImage(CGFloat alpha, void (^setFillColor)(CGContextRef context))
    {
        CGRect rect = CGRectMake(0, 0, 0.5, 0.5);
        UIGraphicsBeginImageContextWithOptions(rect.size, alpha == 1, 0);
        CGContextRef context = UIGraphicsGetCurrentContext();
        setFillColor(context);
        CGContextFillRect(context, rect);
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
    

    Note: ACU is the class prefix of my Cocoa Touch Static Library called Acani Utilities, where AC is for Acani, and U is for Utilities.

  4. Here’s an approach in Swift, using a UIButton extension to add an IBInspectable, called highlightedBackgroundColor. Similar to subclassing, without requiring a subclass.

    private var HighlightedBackgroundColorKey = 0
    private var NormalBackgroundColorKey = 0
    
    extension UIButton {
    
        @IBInspectable var highlightedBackgroundColor: UIColor? {
            get {
                return objc_getAssociatedObject(self, &HighlightedBackgroundColorKey) as? UIColor
            }
    
            set(newValue) {
                objc_setAssociatedObject(self,
                    &HighlightedBackgroundColorKey, newValue, UInt(OBJC_ASSOCIATION_RETAIN))
            }
        }
    
        private var normalBackgroundColor: UIColor? {
            get {
                return objc_getAssociatedObject(self, &NormalBackgroundColorKey) as? UIColor
            }
    
            set(newValue) {
                objc_setAssociatedObject(self,
                    &NormalBackgroundColorKey, newValue, UInt(OBJC_ASSOCIATION_RETAIN))
            }
        }
    
        override public var backgroundColor: UIColor? {
            didSet {
                if !highlighted {
                    normalBackgroundColor = backgroundColor
                }
            }
        }
    
        override public var highlighted: Bool {
            didSet {
                if let highlightedBackgroundColor = self.highlightedBackgroundColor {
                    if highlighted {
                        backgroundColor = highlightedBackgroundColor
                    } else {
                        backgroundColor = normalBackgroundColor
                    }
                }
            }
        }
    }
    

    I hope this helps.

  5. Aleksejs Mjaliks on November 30, -0001 @ 12:00 AM

    There is no need to override highlighted as computed property. You can use property observer to trigger background color change:

    override var highlighted: Bool {
        didSet {
            if highlighted {
                backgroundColor = UIColor.lightGrayColor()
            } else {
                backgroundColor = UIColor.whiteColor()
            }
        }
    }
    
  6. Override highlighted variable.
    Adding @IBInspectable makes you edit the highlighted backgroundColor in storyboard, which is nifty too.

    class BackgroundHighlightedButton: UIButton {
        @IBInspectable var highlightedBackgroundColor :UIColor?
        @IBInspectable var nonHighlightedBackgroundColor :UIColor?
        override var highlighted :Bool {
            get {
                return super.highlighted
            }
            set {
                if newValue {
                    self.backgroundColor = highlightedBackgroundColor
                }
                else {
                    self.backgroundColor = nonHighlightedBackgroundColor
                }
                super.highlighted = newValue
            }
        }
    }
    
  7. You can use this category which add the method setBackgroundColor:forState:

    https://github.com/damienromito/UIButton-setBackgroundColor-forState-

  8. Recycled Steel on November 30, -0001 @ 12:00 AM

    You can subclass the UIButton and make a nice forState.

    colourButton.h

    #import <UIKit/UIKit.h>
    
    @interface colourButton : UIButton
    
    -(void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state;
    
    @end
    

    colourButton.m

    #import "colourButton.h"
    
    @implementation colourButton
    {
        NSMutableDictionary *colours;
    }
    
    -(id)initWithCoder:(NSCoder *)aDecoder
    {
        self = [super initWithCoder:aDecoder];
    
        // If colours does not exist
        if(!colours)
        {
            colours = [NSMutableDictionary new];  // The dictionary is used to store the colour, the key is a text version of the ENUM
            colours[[NSString stringWithFormat:@"%lu", UIControlStateNormal]] = (UIColor*)self.backgroundColor;  // Store the original background colour
        }
    
        return self;
    }
    
    -(void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state
    {
        // If it is normal then set the standard background here
        if(state & UIControlStateNormal)
        {
            [super setBackgroundColor:backgroundColor];
        }
    
        // Store the background colour for that state
        colours[[NSString stringWithFormat:@"%lu", state]]= backgroundColor;
    }
    
    -(void)setHighlighted:(BOOL)highlighted
    {
        // Do original Highlight
        [super setHighlighted:highlighted];
    
        // Highlight with new colour OR replace with orignial
        if (highlighted && colours[[NSString stringWithFormat:@"%lu", UIControlStateHighlighted]])
        {
            self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateHighlighted]];
        }
        else
        {
            self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateNormal]];
        }
    }
    
    -(void)setSelected:(BOOL)selected
    {
        // Do original Selected
        [super setSelected:selected];
    
        // Select with new colour OR replace with orignial
        if (selected && colours[[NSString stringWithFormat:@"%lu", UIControlStateSelected]])
        {
            self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateSelected]];
        }
        else
        {
            self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateNormal]];
        }
    }
    
    @end
    

    Notes (This is an example, I know there are problems and here are some)

    I have used an NSMutableDictionay to store the UIColor for each State, I have to do a nasty text conversion for the Key as the UIControlState is not a nice straight Int. If it where you could init an Array with that many objects and use the State as an index.

    Because of this you many have difficulties with e.g. a selected & disabled button, some more logic is needed.

    Another problem is if you try and set multiple colours at the same time, I have not tried with a button but if you can do this it may not work

     [btn setBackgroundColor:colour forState:UIControlStateSelected & UIControlStateHighlighted];
    

    I have assumed this is StoryBoard, there is no init, initWithFrame so add them if you need them.

  9. Giordano Scalzo on November 30, -0001 @ 12:00 AM

    An handy generic extension in Swift:

    extension UIButton {
        private func imageWithColor(color: UIColor) -> UIImage {
            let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
            UIGraphicsBeginImageContext(rect.size)
            let context = UIGraphicsGetCurrentContext()
    
            CGContextSetFillColorWithColor(context, color.CGColor)
            CGContextFillRect(context, rect)
    
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
    
            return image
        }
    
        func setBackgroundColor(color: UIColor, forUIControlState state: UIControlState) {
            self.setBackgroundImage(imageWithColor(color), forState: state)
        }
    }
    
  10. Try this if you have an image:

    -(void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state;
    

    or see if showsTouchWhenHighlighted is enough for you.

  11. In swift you can override the accessor of the highlighted (or selected) property rather than overriding the setHighlighted method

    override var highlighted: Bool {
            get {
                return super.highlighted
            }
            set {
                if newValue {
                    backgroundColor = UIColor.blackColor()
                }
                else {
                    backgroundColor = UIColor.whiteColor()
                }
                super.highlighted = newValue
            }
        }
    
  12. Not sure if this sort of solves what you’re after, or fits with your general development landscape but the first thing I would try would be to change the background colour of the button on the touchDown event.

    Option 1:
    You would need two events to be capture, UIControlEventTouchDown would be for when the user presses the button. UIControlEventTouchUpInside and UIControlEventTouchUpOutside will be for when they release the button to return it to the normal state

    UIButton *myButton =  [UIButton buttonWithType:UIButtonTypeCustom];
    [myButton setFrame:CGRectMake(10.0f, 10.0f, 100.0f, 20.f)];
    [myButton setBackgroundColor:[UIColor blueColor]];
    [myButton setTitle:@"click me:" forState:UIControlStateNormal];
    [myButton setTitle:@"changed" forState:UIControlStateHighlighted];
    [myButton addTarget:self action:@selector(buttonHighlight:) forControlEvents:UIControlEventTouchDown];
    [myButton addTarget:self action:@selector(buttonNormal:) forControlEvents:UIControlEventTouchUpInside];
    

    Option 2:

    Return an image made from the highlight colour you want. This could also be a category.

    + (UIImage *)imageWithColor:(UIColor *)color {
       CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
       UIGraphicsBeginImageContext(rect.size);
       CGContextRef context = UIGraphicsGetCurrentContext();
    
       CGContextSetFillColorWithColor(context, [color CGColor]);
       CGContextFillRect(context, rect);
    
       UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
       UIGraphicsEndImageContext();
    
       return image;
    }
    

    and then change the highlighted state of the button:

    [myButton setBackgroundImage:[self imageWithColor:[UIColor greenColor]] forState:UIControlStateHighlighted];
    
  13. You can override UIButton :

    - (void) setHighlighted:(BOOL)highlighted {
        [super setHighlighted:highlighted];
    
        if (highlighted) {
            self.backgroundColor = UIColorFromRGB(0x387038);
        }
        else {
            self.backgroundColor = UIColorFromRGB(0x5bb75b);
        }
    }
    
  14. Shashank shree on November 30, -0001 @ 12:00 AM

    It is possible by set it through XIB.
    In xib set Highlighted Tint color as u desire while tapping on Button.
    As there is not programmatically right approach for this till now.
    So it is easy for you by set it in XIB .

    It will works fine :) enjoy..

  15. Try tintColor:

    _button.tintColor = [UIColor redColor];
    

Leave a Reply