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
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];
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.
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.
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];
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?
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.