Is it possible to change a UIButtons background color? - objective-c

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

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

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>

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.

Another possibility:
Create a UIButton in Interface builder.
Give it a type 'Custom'
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

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];
}

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;

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!

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

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

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];
}

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

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.

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.

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()

[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.

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)

For iOS 15+ Apple provides a simple button configuration to accomplish this.
Objective-C:
randomButton.configuration = [UIButtonConfiguration filledButtonConfiguration];
Swift:
randomButton.configuration = .filled()

Related

setting background on UIButton on Map Callout

Hi I am trying to set a background image on my detail disclosure UIButton attached to my MapView MKAnnotation callout view. Checking on stackoverflow there are consistent examples of how to set this, however it doesn't seem to work for my callout UIButton. My question is, does this not work, simply because of the mapview or am I missing something else?
Here is my code:
// If an existing pin view was not available, create one.
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = YES;
pinView.image = [UIImage imageNamed:#"Tch4"];
//pinView.calloutOffset = CGPointMake(0, 32);
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
UIImage *btnImage = [UIImage imageNamed:#"4stars"];
[pinView.rightCalloutAccessoryView setBackgroundImage:btnImage forState:UIControlStateNormal];
I receive the error on the final line. Error is: No Visable interface for UIView declares the selector 'setbackgroundimage: forstate'
Thanks for your help with this
Your problem is rightCalloutAccessoryView returns a UIView instance. And UIView does not respond to setBackgroundImage:forState: method.
What you are looking for is to add a UIButton instead something like this:
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
infoButton.frame = CGRectMake(0, 0, 32, 32); // Set your own frame
[infoButton setBackgroundImage:btnImage forState:UIControlStateNormal];
[infoButton setBackgroundImage:btnImage forState:UIControlStateSelected];
pinView.rightCalloutAccessoryView = infoButton;
you cant set background image if your typebutton DetailDisclosure.
you can addsubview image to rightCalloutAccessoryView but without button..
so if you still use typebutton DetailDisclosure you cant set background

Rounded UIButton corner leads to weird appearance

I am generating my custom view controller's view programmatically, including adding a few buttons. I have three buttons in a row, all created along the lines of this one:
self.futureButton = [[UIButton alloc] initWithFrame:CGRectMake(0, buttonOffset, _width/3, _height*.1)];
self.futureButton.backgroundColor = [UIColor pinkColor];
self.futureButton.tag = 1;
[self.futureButton setTitle:#"Upcoming" forState: UIControlStateNormal];
[self.futureButton setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
[self setColor:[UIColor lightPinkColor] forState:UIControlStateSelected forButton:self.futureButton];
[self.futureButton addTarget:self action:#selector(changAppt:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.futureButton];
I also do this at the end to apply the same properties to all these buttons:
for(UIButton *myButton in self.view.subviews){
if([myButton isKindOfClass:[UIButton class]]){
[[myButton layer] setBorderWidth:4.0f];
[[myButton layer] setBorderColor:[UIColor whiteColor].CGColor];
[myButton.layer setCornerRadius:12.0f];<----problem
}
}
Everything is working great except the last command, setCornerRadius. If I comment it out, I have what I want - all the buttons lined up, white border that disappears into the background white. However if I use this last command, setCornerRadius, I get the following (see image)
Also the weird effect is on whichever button is selected. Do I have to do something about the selected state effects and the CornerRadius? If so, what?
Please add
myButton.clipsToBounds = YES;
and this is it :)
If you are interested in more details read this.

how to dim background view

trying to dim background view when I work with custom modal view controller I set background color to grey color with 0.5 opacity:
- (void)rulesTapped:(id)sender
{
RulesVC *rulesVC = [[RulesVC alloc] init];
self.view.backgroundColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:0.5];
rulesVC.modalPresentationStyle = UIModalPresentationCustom;
rulesVC.transitioningDelegate = self;
[self presentViewController:rulesVC animated:YES completion:nil];
}
but when I then try to set it back to whiteColor (or yellow) while dismissing the modal VC, this dimming keeps in place:
- (void) gotItTapped:(id)sender
{
[self.presentingViewController.view setBackgroundColor:[UIColor yellowColor]];
[self dismissViewControllerAnimated:YES completion:nil];
}
Why? And is there way to dim background (and then remove dimming) view without adding another overlaying view but just operating with existing view?
I don't think you can achieve it without adding another element to your UIViewController. I normally insert an UIView which is setHidden:YES and alpha 0.0f and blackColor as backgroundColor. When showing something, I always set it Hidden:NO and animate it to opacity 0.6f.
[UIView animateWithDuration:0.5f animations:^{
[self.view setAlpha:0.6];
}];
This way it appears not immediately and looks much smoother.
I actually needed this to be able to put some other UIView in dim view, and that every click outside this dimview, would dismiss both built-in UIView and dimView. What I've discovered, is that for this the best is to imitate a button which is removed from superview upon click:
self.dimView = [UIButton buttonWithType:UIButtonTypeCustom];
self.dimView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2f];
self.dimView.frame = CGRectMake(0,
0,
self.view.frame.size.width,
self.view.frame.size.height);
[self.dimView addTarget:self
action:#selector(removeDimView)
forControlEvents:UIControlEventTouchUpInside];

UIInputView background is invisible while keyboard is animating

I have a text field which has a UIInputView input accessory view.
When I tap my text field, and the keyboard comes flying into view and I can see the accessory view's subviews, but it has no visible background.
Once the keyboard animation is complete, the UIInputView's background pops into view.
What can I do to force the UIInputView's background to be visible while the keyboard animation is still going?
Here's my code:
UIInputView *inputView = [[UIInputView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.bounds), 44.0f) inputViewStyle:UIInputViewStyleKeyboard];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.frame = CGRectInset(inputView.bounds, 15.0f, 2.0f);
[button setTitle:#"Button" forState:UIControlStateNormal];
[inputView addSubview:button];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.bounds), 44.0f)];
textField.inputAccessoryView = inputView;
[self addSubview:textField];
Not a complete solution, but this impact can be minimized if you set background of your UIInputView manually inside -viewDidLoad: method. Fortunately, no need to remove it after. Right after appearing UIInputView will have a truly keyboard background and a blur effect as well.
UIColor *tmpBackground; //Keyboard have color base on the current device.
if ([UIDevice isPad] || (UIDevice isPod)]) { //These are my helpers, you have to implement it
tmpBackground = [UIColor colorWithRed:207/255.0f green:210/255.0f blue:214/255.0f alpha:1];
} else {
tmpBackground = [UIColor colorWithRed:216/255.0f green:219/255.0f blue:223/255.0f alpha:1];
}
[textField.inputAccessoryView setBackgroundColor:tmpBackground];
P.S. And I know, it is not a perfect solution to define colors in such a stupid way.

Shows Touch On Highlight for UIImage

The UIButton has a cool attribute that is called "Shows Touch On Highlight" that allows me to tell the user "you touched this button".
Is there any way I can implement this for a UIImage? I want the user to see the point touched inside the UIImage.
If you are using a little trick. make a transparent button above UIImageView. so must set a UIImageView userInteractionEnabled true bec default false. as follows:
imageView.userInteractionEnabled = YES;
UIButton *clearButton = [UIButton buttonWithType:UIButtonTypeCustom];
clearButton.frame = imageView.frame;
clearButton.showsTouchWhenHighlighted = YES;
[clearButton setBackgroundColor:[UIColor clearColor]];
[imageView addSubView:clearButton];
other trick, create a transparent button, and set image your want.
UIButton *clearButton = [UIButton buttonWithType:UIButtonTypeCustom];
clearButton.frame = CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>);
clearButton.showsTouchWhenHighlighted = YES;
[clearButton setImage:[UIImage imageNamed:#"your_image"] forState:UIControlStateNormal];
[clearButton setBackgroundColor:[UIColor clearColor]];
No, it doesn't in same way as in UIButton, but you can use UIButton of type custom, instead to solve it for you.
Another help with UIImageView is setting it's Highlight image, but still you have to work for effect.