Reputation:
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?
Upvotes: 37
Views: 85886
Reputation: 2303
May be I misunderstood your question, but does below not work for you?
Upvotes: -2
Reputation: 31
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.
Upvotes: 0
Reputation: 3960
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.
Upvotes: 0
Reputation: 649
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.
Upvotes: 2
Reputation: 11
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];
Upvotes: 1
Reputation: 2484
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];
Upvotes: 10
Reputation: 2836
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;
Upvotes: 0
Reputation: 540
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.
Upvotes: 1
Reputation: 17581
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
Upvotes: 1
Reputation: 5435
http://github.com/dermdaly/ButtonMaker
You can generate UIbutton image using this application...and can set it as background image of your button
Upvotes: 9
Reputation: 11
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.
Upvotes: 1
Reputation: 9429
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?
Upvotes: 4
Reputation: 1613
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];
Upvotes: 20
Reputation:
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.
Upvotes: 1
Reputation: 7605
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.
Upvotes: 0
Reputation: 325
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).
Upvotes: 5
Reputation: 70997
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 :)
Upvotes: 15
Reputation: 170319
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];
Upvotes: 16