107

This one has me stumped.

Is it possible at all to change the background color of a UIButton in Cocoa for iPhone. I've tried setting the background color but it only changes the corners. setBackgroundColor: seems to be the only method available for such things.

[random setBackgroundColor:[UIColor blueColor]];
[random.titleLabel setBackgroundColor:[UIColor blueColor]];
squ1dd13
  • 84
  • 2
  • 9
dubbeat
  • 7,706
  • 18
  • 70
  • 122
  • This is a duplicate of http://stackoverflow.com/questions/372731/how-can-i-set-a-button-background-color-on-iphone – Brad Larson May 11 '10 at 12:38
  • I solve this issue in my previos post please check the link [Dynamically change border or backgraound of an UIButton][1] [1]: http://stackoverflow.com/questions/9733213/how-to-change-background-color-in-monotouch/11753717#11753717 – IMMORTAL Aug 01 '12 at 06:34
  • good tutorial and sample code found here http://www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers/ – Shamsudheen TK Jan 21 '13 at 19:17
  • This is for pre iOS7 - in iOS7 setting the background color works as expected and the below code is not needed – brainray Jan 10 '14 at 20:54
  • Check this post https://somethingaboutios.wordpress.com/2016/02/09/uibutton-backgroundcolor-for-uicontrolstateselected/ – Gabriel.Massana Feb 09 '16 at 21:08

17 Answers17

161

This can be done programmatically by making a replica:

loginButton = [UIButton buttonWithType:UIButtonTypeCustom];
[loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
loginButton.backgroundColor = [UIColor whiteColor];
loginButton.layer.borderColor = [UIColor blackColor].CGColor;
loginButton.layer.borderWidth = 0.5f;
loginButton.layer.cornerRadius = 10.0f;

edit: of course, you'd have to #import <QuartzCore/QuartzCore.h>

edit: to all new readers, you should also consider a few options added as "another possibility". for you consideration.

As this is an old answer, I strongly recommend reading comments for troubleshooting

shim
  • 9,289
  • 12
  • 69
  • 108
Stian Storrvik
  • 2,199
  • 2
  • 14
  • 21
  • 4
    sweet! Of all the solns I found this is the one I went with. As a point of clarification, making a 'replica' means using Quartz to draw a rounded rectangle in a custom button, rather than using a RoundedRect-type button. At first I thought it meant somehow making a duplicate of a rounded rect with a different color to simulate changing the bg color. – daver Dec 01 '11 at 03:17
  • 1
    Worked perfectly, best answer to OP. Thanks for sharing. – Bram Mar 21 '12 at 16:35
  • @daver: This answer is so popular it must have merit I am missing. _How can one change the background colour according to state?_ Presumably one must explicitly change background colour every time the state changes. My button is disabled on press until a web service responds. Must I change to highlight colour on press and have a timer change to disabled colour while waiting for the ws to respond? If I am missing the point can someone please explain. Thanks Polly. – Polly Aug 11 '12 at 11:05
  • FYI for others take note of UIButtonTypeCustom, this wont work on default style buttons. – owen gerig Aug 16 '12 at 18:26
  • Does this need to be updated? The snippet posted does not make a buttons background content to be of any color (only the edges). – AlvinfromDiaspar Aug 27 '12 at 14:54
  • sorry for not answering in a long time; Polly: you have to do some custom design here, but this is the outline that you can use. for you specific use-case, you should experiment and see what works best imo. @AlvinfromDiaspar: you swap out the "whitecolor" with whichever color you want. Use RGB to create the color you want if it's not standard. hope this helps. – Stian Storrvik Aug 31 '12 at 15:05
  • Also needs to be set as UIButtonTypeCustom in the xib file. Changing it programmatically doesn't seem to work. Please update the post if possible as these comments are not very visible. – Wayne Shelley Apr 11 '13 at 15:28
  • I do not get the expected result, as the background image goes green and instantly stays the original color. How can I permanently change the color and only remove when I want? Thanks – mmm Jun 18 '13 at 19:48
  • 1
    @Polly: Sorry didn't see your comment sooner, but if you're still interested here's what I ended up doing: subclass UIButton and override drawRect: to draw the rounded rect using some UIBezierPaths. After drawing, fill it with a gradient using CGGradientCreateWithColorComponents(). One of the args to this function is a 4 element array of floats (corresponding to R, G, B, & alpha). I defn'ed 2 color arrays, one for each state, and chose the appropriate one in drawRect based on the button state. – daver Aug 13 '13 at 02:25
  • The important thing to note is that layer.borderColor takes a CGColor, not a UIColor! Therefore you cannot add it as a dynamic property in the identity inspector - unlike layer.cornerRadius and layer.borderWidth – RndmTsk Jun 29 '15 at 19:01
  • .backgroundColor was all I needed –  Jul 30 '18 at 07:34
60

I have a different approach,

[btFind setTitle:NSLocalizedString(@"Find", @"") forState:UIControlStateNormal];    
[btFind setBackgroundImage:[CommonUIUtility imageFromColor:[UIColor cyanColor]] 
        forState:UIControlStateNormal];
btFind.layer.cornerRadius = 8.0;
btFind.layer.masksToBounds = YES;
btFind.layer.borderColor = [UIColor lightGrayColor].CGColor;
btFind.layer.borderWidth = 1;

From CommonUIUtility,

+ (UIImage *) imageFromColor:(UIColor *)color {
    CGRect rect = CGRectMake(0, 0, 1, 1);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    //  [[UIColor colorWithRed:222./255 green:227./255 blue: 229./255 alpha:1] CGColor]) ;
    CGContextFillRect(context, rect);
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}

Don't forget to #import <QuartzCore/QuartzCore.h>

karim
  • 15,408
  • 7
  • 58
  • 96
  • 2
    Nice one. I found I had to add a `btFind.layer.borderWidth = 1;` – DefenestrationDay Feb 27 '12 at 22:05
  • I like this solution, but at the moment I can't use it. I get an error, because "CommonUIUtility" can't be found. Google just gives me eclipse stuff, but i guess it should be in the QuartzCore? Any ideas? – Stephan Apr 11 '12 at 09:37
  • No, that's a user defined class for doing common UI stuffs in the app. You write the imageFromColor method in you own class. – karim Apr 15 '12 at 09:24
  • Cripes, that was harder than it should have been. Thanks for a great solution. – Adam Waite Mar 28 '13 at 17:23
  • 1
    Smells like a category method on UIButton: `setBackgroundColor:(UIColor *) forState:(UIControlState)`. – EthanB Aug 20 '13 at 16:34
  • 3
    It's so silly that we're on iOS 7 and this is STILL the cleanest way to add a background color – dmur Oct 03 '13 at 00:21
32

I assume you're talking about a UIButton with UIButtonTypeRoundedRect? You can't change the background color of that. When you try changing it's background color you're rather changing the color of the rect the button is drawn on (which is usually clear). So there are two ways to go. Either you subclass UIButton and overwrite its -drawRect: method or you create images for the different button states (which is perfectly fine to do).

If you set the background images in Interface Builder you should notice that IB doesn't support setting images for all the states the button can have, so I recommend setting the images in code like this:

UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
[myButton setBackgroundImage:[UIImage imageNamed:@"normal.png"] forState:UIControlStateNormal];
[myButton setBackgroundImage:[UIImage imageNamed:@"disabled.png"] forState:UIControlStateDisabled];
[myButton setBackgroundImage:[UIImage imageNamed:@"selected.png"] forState:UIControlStateSelected];
[myButton setBackgroundImage:[UIImage imageNamed:@"higligted.png"] forState:UIControlStateHighlighted];
[myButton setBackgroundImage:[UIImage imageNamed:@"highlighted+selected.png"] forState:(UIControlStateHighlighted | UIControlStateSelected)];

The last line shows how to set an image for the selected & highlighted state (that's the one IB can't set). You don't need the selected images (line 4 & 6) if you're button dosn't need a selected state.

stigi
  • 6,661
  • 3
  • 40
  • 50
  • Thanks for explanation. Just a quick question. If I'm overwriting the drawrect method I guess that would involve me using a drawing library? Something alone the lines of programmatically drawing a rounded rectangle and filling it with a color? – dubbeat May 11 '10 at 09:37
  • 2
    I just did a short google query for "iphone drawrect round corners" and found this: http://iphonedevelopment.blogspot.com/2008/11/creating-transparent-uiviews-rounded.html Check the drawrect method for an example how to draw a round rect. You can use `CGContextSetFillColorWithColor` and `CGContextFillPath` to fill the path drawn by `CGContextAddArcToPoint`. – stigi May 11 '10 at 09:53
  • 2
    +50 for pointing out the bitmasking potential for control states, i.e.: `forState:(UIControlStateHighlighted | UIControlStateSelected)` Cheers. – NSTJ May 21 '14 at 08:22
29

Another possibility:

  1. Create a UIButton in Interface builder.
  2. Give it a type 'Custom'
  3. Now, in IB it is possible to change the background color

However, the button is square, and that is not what we want. Create an IBOutlet with a reference to this button and add the following to the viewDidLoad method:

[buttonOutlet.layer setCornerRadius:7.0f];
[buttonOutlet.layer setClipToBounds:YES];

Don't forget to import QuartzCore.h

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
wubbe
  • 361
  • 4
  • 3
  • 7
    Perfect, thank you! I'm not sure if it's because of the iOS version I'm using (5.1) but setClipToBounds was not available. Instead I used buttonOutlet.layer.masksToBounds = TRUE; – iforce2d May 15 '12 at 18:30
  • When I add this code, it shows a rounded rect, but when i click on it, the button does not reverse highlight. What is going on? – Will Feb 05 '13 at 18:41
  • The problem with this approach is that you can't set a background color for the highlighted state of the button. – ch3rryc0ke Jun 25 '13 at 01:24
17

Subclass UIButton and override setHighlighted and setSelected methods

-(void) setHighlighted:(BOOL)highlighted {

  if(highlighted) {
    self.backgroundColor = [self.mainColor darkerShade];
  } else {
    self.backgroundColor = self.mainColor;
  }
  [super setHighlighted:highlighted];
}

-(void) setSelected:(BOOL)selected {

  if(selected) {
    self.backgroundColor = [self.mainColor darkerShade];
  } else {
    self.backgroundColor = self.mainColor;
  }
  [super setSelected:selected];
}

My darkerShade method is in a UIColor category like this

-(UIColor*) darkerShade {

  float red, green, blue, alpha;
  [self getRed:&red green:&green blue:&blue alpha:&alpha];

  double multiplier = 0.8f;
  return [UIColor colorWithRed:red * multiplier green:green * multiplier blue:blue*multiplier alpha:alpha];
}
Mugunth
  • 14,461
  • 15
  • 66
  • 94
7

Another possibility (the best and most beautiful imho):

Create a UISegmentedControl with 2 segments in the required background color in Interface Builder. Set the type to 'bar'. Then, change it to having only one segment. Interface builder does not accept one segment so you have to do that programmatically.

Therefore, create an IBOutlet for this button and add this to the viewDidLoad of your view:

[segmentedButton removeSegmentAtIndex:1 animated:NO];

Now you have a beautiful glossy, colored button with the specified background color. For actions, use the 'value changed' event.

(I have found this on http://chris-software.com/index.php/2009/05/13/creating-a-nice-glass-buttons/). Thanks Chris!

sbonami
  • 1,892
  • 17
  • 33
wubbe
  • 361
  • 4
  • 3
7

colored UIButton

If you are not wanting to use images, and want it to look exactly like the Rounded Rect style, try this. Just place a UIView over the UIButton, with an identical frame and auto resize mask, set the alpha to 0.3, and set the background to a color. Then use the snippet below to clip the rounded edges off the colored overlay view. Also, uncheck the 'User Interaction Enabled' checkbox in IB on the UIView to allow touch events to cascade down to the UIButton underneath.

One side effect is that your text will also be colorized.

#import <QuartzCore/QuartzCore.h>

colorizeOverlayView.layer.cornerRadius = 10.0f;
colorizeOverlayView.layer.masksToBounds = YES;
Jacob Jennings
  • 2,796
  • 1
  • 24
  • 26
5

Well I'm 99% percent positive that you cannot just go and change the background color of a UIButton. Instead you have to go and change the background images yourself which I think is a pain. I'm amazed that I had to do this.

If I'm wrong or if theres a better way without having to set background images please let me know

[random setBackgroundImage:[UIImage imageNamed:@"toggleoff.png"] forState:UIControlStateNormal];
    [random setTitleColor:[UIColor darkTextColor] forState:UIControlStateNormal];


[random setBackgroundImage:[UIImage imageNamed:@"toggleon.png"] forState:UIControlStateNormal];
    [random setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
dubbeat
  • 7,706
  • 18
  • 70
  • 122
  • 5
    You're absolutely right. It is a pain, and something that should take a few mins to do instead of the hassle of creating images. Plus if your button is round, it makes it even harder to do as well... Such a pain. – Henley Aug 07 '11 at 17:00
2

You can also add a CALayer to the button - you can do lots of things with these including a color overlay, this example uses a plain color layer you can also easily graduate the colour. Be aware though added layers obscure those underneath

+(void)makeButtonColored:(UIButton*)button color1:(UIColor*) color
{

    CALayer *layer = button.layer;
    layer.cornerRadius = 8.0f;
    layer.masksToBounds = YES;
    layer.borderWidth = 4.0f;
    layer.opacity = .3;//
    layer.borderColor = [UIColor colorWithWhite:0.4f alpha:0.2f].CGColor;

    CAGradientLayer *colorLayer = [CAGradientLayer layer];
    colorLayer.cornerRadius = 8.0f;
    colorLayer.frame = button.layer.bounds;
    //set gradient colors
    colorLayer.colors = [NSArray arrayWithObjects:
                         (id) color.CGColor,
                         (id) color.CGColor,
                         nil];

    //set gradient locations
    colorLayer.locations = [NSArray arrayWithObjects:
                            [NSNumber numberWithFloat:0.0f],
                            [NSNumber numberWithFloat:1.0f],
                            nil];


    [button.layer addSublayer:colorLayer];

}
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64
gheese
  • 1,170
  • 1
  • 11
  • 17
  • Brilliant!!!!!! But couldn't be called several times against same button. Could, but new layers are added again and again. – Valeriy Van Jun 26 '13 at 19:22
  • @StanJames: Edits to code should be suggested by a comment (or, if appropriate, by your own answer). – mafso Oct 19 '14 at 19:59
2

Per @EthanB suggestion and @karim making a back filled rectangle, I just created a category for the UIButton to achieve this.

Just drop in the Category code: https://github.com/zmonteca/UIButton-PLColor

Usage:

[button setBackgroundColor:uiTextColor forState:UIControlStateDisabled];

Optional forStates to use:

UIControlStateNormal
UIControlStateHighlighted
UIControlStateDisabled
UIControlStateSelected
zmonteca
  • 2,304
  • 1
  • 26
  • 26
1

For professional and nice looking buttons, you may check this custom button component. You can use it directly in your views and tableviews or modify the source code to make it meet your needs. Hope this helps.

Ahmet Ardal
  • 1,162
  • 1
  • 11
  • 12
1

This isn't as elegant as sub-classing UIButton, however if you just want something quick - what I did was create custom button, then a 1px by 1px image with the colour I'd want the button to be, and set the background of the button to that image for the highlighted state - works for my needs.

SMSidat
  • 1,163
  • 1
  • 15
  • 34
1

I know this was asked a long time ago and now there's a new UIButtonTypeSystem. But newer questions are being marked as duplicates of this question so here's my newer answer in the context of an iOS 7 system button, use the .tintColor property.

let button = UIButton(type: .System)
button.setTitle("My Button", forState: .Normal)
button.tintColor = .redColor()
DonnaLea
  • 8,643
  • 4
  • 32
  • 32
1

add a second target for the UIButton for UIControlEventTouched and change the UIButton background color. Then change it back in the UIControlEventTouchUpInside target;

pablasso
  • 2,479
  • 2
  • 26
  • 32
Shaun
  • 11
  • 1
0

[myButton setBackgroundColor:[UIColor blueColor]]; [myButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];

It's possible change this way or going on Storyboard and change background on options in right side.

0

Swift 3:

        static func imageFromColor(color: UIColor, width: CGFloat, height: CGFloat) -> UIImage {
            let rect = CGRect(x: 0, y: 0, width: width, height: height)
            UIGraphicsBeginImageContext(rect.size)
            let context = UIGraphicsGetCurrentContext()!
            context.setFillColor(color.cgColor)
            context.fill(rect)
            let img = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            return img
        }

        let button = UIButton(type: .system)
        let image = imageFromColor(color: .red, width: 
        button.frame.size.width, height: button.frame.size.height)
        button.setBackgroundImage(image, for: .normal)
Rudolf Adamkovič
  • 31,030
  • 13
  • 103
  • 118
pawurb
  • 516
  • 6
  • 19
  • This would be a much more useful answer if you said how to invoke or make use of the code you've put up there. Does it go into a UIButton extension? – Michael Dautermann Jun 11 '17 at 06:45
0

For iOS 15+ Apple provides a simple button configuration to accomplish this.

Objective-C:

randomButton.configuration = [UIButtonConfiguration filledButtonConfiguration];

Swift:

randomButton.configuration = .filled()

Curiosity
  • 544
  • 1
  • 15
  • 29