37

How can I set a custom background color of a button?

Interface Builder doesn't seem to have an interface to do this.

Is it only available programmatically? If so, can you provide an example, please?

GEOCHET
  • 20,623
  • 15
  • 71
  • 98

18 Answers18

20

I read your question to require (as I do) a programmatic way to set button color. It's a glaring omission from the UIKit, and I couldn't get the undocumented UIGlassButton to work.

I've solved this with a single segment UISegmentedControl, which allows you to set the tintColor:

UISegmentedControl * btn = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:name]];
btn.momentary = YES;
btn.segmentedControlStyle = UISegmentedControlStyleBar;
btn.tintColor = color;
[btn addTarget:self action:@selector(action:) forControlEvents:UIControlEventValueChanged];

Note please that the momentary and segmentedControlStyle properties do matter, and that an image can be used instead of a NSString * name.

A stretchable image with end caps works fine if you can use a finite set of canned images, but that doesn't fit the requirement! E.g.,

buttonImage   = [[UIImage imageNamed:@"button.png"] stretchableImageWithLeftCapWidth:26 topCapHeight:16];
Tim James
  • 1,353
  • 1
  • 13
  • 15
  • Well, maybe you didn't require a programmatic way. That was what I needed. I expect it works in Interface Builder as well! – Tim James Oct 02 '09 at 00:13
  • 1
    It's a nice solution but unfortunately you don't get the button having the touch down effect when you click/touch on it. – Chris S Mar 05 '10 at 18:34
16

I found that I needed to use a stretchable image to accomplish this. Apple's UICatalog example has one or more colored buttons that are drawn in this fashion. You could use their template image and recolor it to suit your button needs.

I'm not sure about doing this in Interface Builder, but I was able to create a button and use an image for its contents using the following code:

downloadButton = [[UIButton alloc] initWithFrame:CGRectMake(36, 212, 247, 37)];

downloadButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
downloadButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;

[downloadButton setTitle:NSLocalizedStringFromTable(@"Download", @"Localized", nil) forState:UIControlStateNormal]; 
[downloadButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[downloadButton setFont:[UIFont boldSystemFontOfSize:14.0]];

UIImage *newImage = [[UIImage imageNamed:@"greenButton.png"] stretchableImageWithLeftCapWidth:12.0f topCapHeight:0.0f];
[downloadButton setBackgroundImage:newImage forState:UIControlStateNormal];

[downloadButton addTarget:self action:@selector(downloadNewItem) forControlEvents:UIControlEventTouchDown];

downloadButton.backgroundColor = [UIColor clearColor];
[downloadDisplayView addSubview:downloadButton];
Brad Larson
  • 168,330
  • 45
  • 388
  • 563
15

Setting the background color of the view for a rounded-rect button will not change the background color of the button. Try making the button a custom button (the first drop-down in the inspector) and then setting the background color. You will get the desired effect :)

lostInTransit
  • 68,087
  • 58
  • 193
  • 270
  • But how do you keep the button rounded when you do this? – Neo42 Aug 27 '09 at 00:41
  • If you want to keep the button rounded, you'll have to use a custom image as the cell background with the background color set as clear. – lostInTransit Aug 27 '09 at 06:11
  • 5
    You can programmatically set `button.layer.cornerRadius` to get a rounded button (and you need to enable `button.layer.masksToBounds` as well AFAIK). This has been the easiest solution for me. – aerique Dec 15 '11 at 09:54
10

It seems, that button color still is an issue. The following category sets a button's backgroundimage completely programmatically via an instance method that takes a UIColor parameter. There is no need to create button backgroundimage files. It preserves button behavior based on UIControlState. And it keeps the rounded corners.

The header file UIButton+ColoredBackground.h

#import <UIKit/UIKit.h>

@interface UIButton (ColoredBackground)

- (void)setBackgroundImageByColor:(UIColor *)backgroundColor forState:(UIControlState)state;

@end

and the content of UIButton+ColoredBackground.m

#import "UIButton+ColoredBackground.h"
#import <QuartzCore/QuartzCore.h>

@implementation UIButton (ColoredBackground)

- (void)setBackgroundImageByColor:(UIColor *)backgroundColor forState:(UIControlState)state{

    // tcv - temporary colored view
    UIView *tcv = [[UIView alloc] initWithFrame:self.frame];
    [tcv setBackgroundColor:backgroundColor];

    // set up a graphics context of button's size
    CGSize gcSize = tcv.frame.size;
    UIGraphicsBeginImageContext(gcSize);
    // add tcv's layer to context
    [tcv.layer renderInContext:UIGraphicsGetCurrentContext()];
    // create background image now
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // set image as button's background image for the given state
    [self setBackgroundImage:image forState:state];
    UIGraphicsEndImageContext();

    // ensure rounded button
    self.clipsToBounds = YES;
    self.layer.cornerRadius = 8.0;

    [tcv release];

}

@end

The new method is justed called on every UIButton instance like:

[myButton setBackgroundImageByColor:[UIColor greenColor] forState:UIControlStateNormal];
alex
  • 2,386
  • 22
  • 27
9

http://github.com/dermdaly/ButtonMaker

You can generate UIbutton image using this application...and can set it as background image of your button

S.P.
  • 5,379
  • 10
  • 54
  • 82
  • I found that the image created by this wasn't quite the same shape as a rounded rect button. I switched to custom and it worked nicely. – Tommy Herbert Nov 08 '10 at 15:32
5

Set your button type to "custom" in the button attributes palette. This will give you a square button with whatever color you picked for the background.

If you want a button that looks more like a traditional button, but has a different color your going to need to go into some kind of image editing software and create it (I use photoshop for custom buttons).

acreek
  • 315
  • 1
  • 3
  • 8
4

To keep the button rounded, what about using

view.layer.cornerRadius = 8;

described here

How do I create a round cornered UILabel on the iPhone?

Community
  • 1
  • 1
Alex
  • 8,838
  • 11
  • 67
  • 79
  • 1
    This totally works for me. Just be sure to include the following line. #import mj – mj_ Aug 24 '10 at 18:36
2

i have a little modified the code version of @Patch. The problem for me was that the old version stretched the corner radius kind of dirty when switching from portrait to landscape mode. So i made this version use stretchableImageWithLeftCapWidth:topCapHeight Method.

#import "UIButton+ColoredBackground.h"
#import <QuartzCore/QuartzCore.h>

#define kCornerRadius 8.0f
#define kStrokeColor [UIColor blackColor]
#define kStrokeWidth 1.0f

@implementation UIButton (ColoredBackground)

- (void)setBackgroundImageByColor:(UIColor *)backgroundColor forState:(UIControlState)state{


CGSize gcSize = CGSizeMake(2*kCornerRadius +1, self.bounds.size.height);
UIGraphicsBeginImageContext(gcSize);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeOverlay);



CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();

UIColor *gradientStart = [UIColor colorWithRed:0.80 green:0.80 blue:0.80 alpha:0.2];
UIColor * gradientEnd = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.2];
NSArray *colors = [NSArray arrayWithObjects:(id)gradientStart.CGColor, (id)gradientEnd.CGColor, nil];
CGFloat locations[2] = { 0.0f, 1.0f };
CGGradientRef _gradient = CGGradientCreateWithColors(rgb, (__bridge CFArrayRef)colors, locations);
CGColorSpaceRelease(rgb);
CGContextDrawLinearGradient(UIGraphicsGetCurrentContext(), _gradient, CGPointMake(0.0, kStrokeWidth), CGPointMake(0.0, self.bounds.size.height-kStrokeWidth), 0);

UIBezierPath *outsideEdge = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.0, 0.0, gcSize.width, gcSize.height) cornerRadius:kCornerRadius];
[backgroundColor setFill];
[kStrokeColor setStroke];
outsideEdge.lineWidth = kStrokeWidth;
[outsideEdge stroke];
[outsideEdge fill];

// Create the background image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    
UIGraphicsEndImageContext(); 

// Set image as button's background image (stretchable) for the given state
[self setBackgroundImage:[image stretchableImageWithLeftCapWidth:kCornerRadius topCapHeight:0.0] forState:state];

// Ensure rounded button
self.clipsToBounds = YES;
self.layer.cornerRadius = kCornerRadius;



}

@end

The usage didn't changed. It looks clearer now on the edges, and i also aded a gradient to make more plastic. but you can remove this as well.

Denny1989
  • 619
  • 8
  • 15
  • I'm trying to use this in IOS and getting: : CGContextSetBlendMode: invalid context 0x0 as well as numerous other invalid context 0x0 errors. Any ideas as to why? – T.J. Jul 08 '13 at 03:09
  • Its seems as if the self.bounds.size.height is 0f when it is called although all my buttons have a height. – T.J. Jul 08 '13 at 03:15
  • Solved problems commented on above. Solution posted below. – T.J. Jul 08 '13 at 17:23
1

Tim James' solution (a single-segment segmented control) worked for me when I wanted a simple 'Save' button with a decent blue gradient background. I simply created a 2 segment control in the nib, set up the first segment as I wanted it and then programatically removed the second segment under -viewWillAppear using [mySegmentedControl removeSegmentAtIndex:1 animated:NO]. For my purposes 'Momentary' needed to be active (ticked) for the Segmented Control's first segment in the nib and 'Selected' was not active for either segment. 'Value Changed' is the Segmented Control's Action to connect up.

iTinck
  • 11
  • 1
1

Here is a free app I created that creates UIGlassButton images. Set the button type to custom. http://itunes.apple.com/us/app/uibutton-builder/id408204223?mt=8

Bryan
  • 15,409
  • 22
  • 92
  • 119
  • Wow, is this a handy little app! I had been using the single-element segmented control trick but this gives me the highlight on press behavior that I was missing. Bravo! – iPhoneDollaraire Mar 30 '11 at 11:20
1

i did it as follows to create button like system delete button as in uitableview


UIButton *h=[[UIButton  alloc]init];
[[h layer] setCornerRadius:8.0f];
[[h layer] setMasksToBounds:YES];
[[h layer] setBorderWidth:1.0f];
CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init];
[gradientLayer setBounds:[h bounds]];
[gradientLayer setPosition:CGPointMake([h bounds].size.width/2, [h bounds].size.height/2)];
[gradientLayer setColors:[NSArray arrayWithObjects:
(id)[[UIColor darkGrayColor]CGColor],(id)[[UIColor blackColor] CGColor], nil]];
[[h layer] insertSublayer:gradientLayer atIndex:0];

release all objects and import QuartzCore/QuartzCore.h and add this framework to your project.

saurabh
  • 500
  • 1
  • 8
  • 21
1

I solved this problem by creating my own button in Fireworks. I drew a rounded rectangle of about the size I wanted, with the fill color and border color I wanted. Trim the canvas, and save as a PNG. In XCode, add the file to the Resources folder. You can use a Custom button in Interface Builder and specify the image as that PNG. It will resize it as needed, put the text on top, etc. The curve of the corners may distort if you have to resize it much from the original.

1

Here's a variation on @user200212's solution (http://stackoverflow.com/a/8547424). It uses the UIBezierCurve class to fill the button and draw a 1px black border around it:

UIButton+ColoredBackground.h

//  UIButton+ColoredBackground.h

#import <UIKit/UIKit.h>

@interface UIButton (ColoredBackground)

- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state;
+ (UIColor *) silverColor;

@end

UIButton+ColoredBackground.m

//  UIButton+ColoredBackground.m
#import "UIButton+ColoredBackground.h"
#import <QuartzCore/QuartzCore.h>

@implementation UIButton (UIButton_ColoredBackground)

- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state{

    CGSize gcSize = self.bounds.size;
    UIGraphicsBeginImageContext(gcSize);

    // Create a Bezier Path around the button, and fill it with the background color
    UIBezierPath *outsideEdge = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:8.0f];
    [backgroundColor setFill];
    [[UIColor blackColor] setStroke];
    outsideEdge.lineWidth = 1.0;
    [outsideEdge fill];
    [outsideEdge stroke];

    // Create the background image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    

    UIGraphicsEndImageContext(); 

    // Set image as button's background image for the given state
    [self setBackgroundImage:image forState:state];

    // Ensure rounded button
    self.clipsToBounds = YES;
    self.layer.cornerRadius = 8.0;
}

@end

Usage:

[myButton setBackgroundImageByColor:[UIColor greenColor] forState:UIControlStateNormal];
Patch
  • 11
  • 2
0

Building further on @Denny1989 I've solved a problem getting errors (invalid context 0x0) when setting up the button in viewDidLoad. The button needs to be laid out before it has a size and thus cannot be depended on until after the view appears. This category adds a color for UIControlStateNormal. If other control states are necessary for your app you need to add them similarly to the category.

//  UIButton+ColoredBackground.h

#import <UIKit/UIKit.h>

@interface UIButton (ColoredBackground)

@property (nonatomic, retain) UIColor *buttonColorNormal;

@property (nonatomic, retain) NSNumber *madeBackgroundImages;

- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state;

- (void)drawRect:(CGRect)rect;

- (void)awakeFromNib;

@end

and

//  UIButton+ColoredBackground.m

#import "UIButton+ColoredBackground.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>

#define kCornerRadius 8.0f
#define kStrokeColor [UIColor darkGrayColor]
#define kStrokeWidth 1.0f



@implementation UIButton (ColoredBackground)

static char UIB_ButtonColorNormal_KEY;
static char UIB_MadeBackgroundImages_KEY;

@dynamic buttonColorNormal;

-(void)setButtonColorNormal:(NSObject *)buttonColorNormal
{
    objc_setAssociatedObject(self, &UIB_ButtonColorNormal_KEY, buttonColorNormal, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(NSObject*)buttonColorNormal
{
    return (NSObject*)objc_getAssociatedObject(self, &UIB_ButtonColorNormal_KEY);
}

@dynamic madeBackgroundImages;

-(void)setMadeBackgroundImages:(NSObject *)madeBackgroundImages
{
    objc_setAssociatedObject(self, &UIB_MadeBackgroundImages_KEY, madeBackgroundImages, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(NSObject*)madeBackgroundImages
{
    return (NSObject*)objc_getAssociatedObject(self, &UIB_MadeBackgroundImages_KEY);
}
- (void)awakeFromNib {
    [self setMadeBackgroundImages:[NSNumber numberWithBool:FALSE]];
    [super awakeFromNib];
}

- (void)drawRect:(CGRect)rect {
    // if background images were not created from color do so here
    // if self.buttonColorNormal is not set behaves like normal button
    if ((self.buttonColorNormal)&&(![[self madeBackgroundImages] boolValue])) {
        [self setBackgroundColor:[self buttonColorNormal] forState:UIControlStateNormal];
        [self setMadeBackgroundImages:[NSNumber numberWithBool:TRUE]];
    }
    [super drawRect:rect];
}

- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state {

    gcSize = CGSizeMake(2*kCornerRadius +1, self.bounds.size.height);
    UIGraphicsBeginImageContext(gcSize);

    CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();

    UIColor *gradientStart = [UIColor colorWithRed:0.80 green:0.80 blue:0.80 alpha:0.2];
    UIColor * gradientEnd = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
    NSArray *colors = [NSArray arrayWithObjects:(id)gradientStart.CGColor, (id)gradientEnd.CGColor, nil];
    CGFloat locations[2] = { 0.0f, 1.0f };
    CGGradientRef _gradient = CGGradientCreateWithColors(rgb, (__bridge CFArrayRef)colors, locations);
    CGColorSpaceRelease(rgb);
    CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeOverlay);
    CGContextDrawLinearGradient(UIGraphicsGetCurrentContext(), _gradient, CGPointMake(0.0, kStrokeWidth), CGPointMake(0.0, self.bounds.size.height-kStrokeWidth), 0);

    UIBezierPath *outsideEdge = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.0, 0.0, gcSize.width, gcSize.height) cornerRadius:kCornerRadius];
    [backgroundColor setFill];
    [kStrokeColor setStroke];
    outsideEdge.lineWidth = kStrokeWidth;
    [outsideEdge stroke];
    [outsideEdge fill];
    CFRelease(_gradient);

    // Create the background image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Set image as button's background image (stretchable) for the given state
    [self setBackgroundImage:[image stretchableImageWithLeftCapWidth:kCornerRadius topCapHeight:0.0] forState:state];

    // Ensure rounded button
    self.clipsToBounds = YES;
    self.layer.cornerRadius = kCornerRadius;

    // add colored border
    self.layer.borderColor = kStrokeColor.CGColor;
    self.layer.borderWidth = kStrokeWidth;
}

@end

usage

- (void)viewDidLoad
{
        [myButton setButtonColorNormal:[UIColor redColor]];
}

Be sure to make the button type custom or drawRect may not be called.

T.J.
  • 3,900
  • 2
  • 29
  • 40
0

So, I found an incredibly simple solution using categories. (So simple that I think I must be doing something hacky, but I don't see it!)

Define a category (if you don't know what that is, in summary: it is a way to "extend" a class minimally; that is, to add methods to a class but no properties) for UIButton, and create a method "setBackgroundColor" that calls UIButton's super class' (UIControl) setBackgroundColor method.

@implementation UIButton (coloredBackgroundButton)

-(void)setBackgroundColor:(UIColor *)backgroundColor {
    [super setBackgroundColor:backgroundColor];
}

Wherever you use UIButtons, as long as you import the header file in which this category is declared, in this case,

#import "coloredBackgroundButton.h" 

you can call setBackgroundColor on them.

0

keremk was right when he said to change the 'view' background color when you have clicked on the button and gone into the inspector. This changes the little corners into whatever colour you picked.

Domness
  • 7,477
  • 8
  • 38
  • 49
0

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,668
  • 1
  • 21
  • 26
-2

May be I misunderstood your question, but does below not work for you?

Laurel
  • 5,522
  • 11
  • 26
  • 49
keremk
  • 2,293
  • 14
  • 10