26

Hi I want to change the UIButton background color which user Tap on the button.

I am showing background color using gradient, when user tap I need to change the gradient color.

[btn setTitle: @"Next" forState:UIControlStateNormal];
CAGradientLayer *gradient = [CAGradientLayer layer];            
gradient.frame = btn.bounds;
gradient.cornerRadius = 10.0f;
locations = [[NSArray alloc] initWithObjects: 0.0f, 0.0f, 0.0f,0.0f, nil]; 
[gradient setLocations:locations];
colorNext = [[NSArray alloc] initWithObjects:…., nil]; 
gradient.colors = colorNext;
[locations release];
[btn.layer insertSublayer:gradient atIndex:0];
btn.titleLabel.textColor = [UIColor blackColor];
mxcl
  • 26,392
  • 12
  • 99
  • 98
iPhoneDev
  • 2,995
  • 4
  • 33
  • 44
  • There are good answers at this question: http://stackoverflow.com/questions/14523348/how-to-change-the-background-color-of-a-uibutton-while-its-highlighted – ThomasW Feb 03 '16 at 02:44

6 Answers6

35

Changing the internal layer hierarchy of a UIButton is not recommended since it may break in a future update of the iOS. Also, it may cause Apple to reject your application. A better solution would be to create a button subclass. Here is an example of how to do it:

@interface MyButton : UIButton
@end

@implementation MyButton

- (id)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        [self addObserver:self forKeyPath:@"highlighted" options:NSKeyValueObservingOptionNew context:NULL];
    }

    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    if (self.highlighted == YES)
    {
        CGContextRef ctx = UIGraphicsGetCurrentContext();

        CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();

        const CGFloat *topGradientColorComponents = CGColorGetComponents([UIColor whiteColor].CGColor);
        const CGFloat *bottomGradientColorComponents = CGColorGetComponents([UIColor blackColor].CGColor);

        CGFloat colors[] =
        {
            topGradientColorComponents[0], topGradientColorComponents[1], topGradientColorComponents[2], topGradientColorComponents[3],
            bottomGradientColorComponents[0], bottomGradientColorComponents[1], bottomGradientColorComponents[2], bottomGradientColorComponents[3]
        };
        CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors) / (sizeof(colors[0]) * 4));
        CGColorSpaceRelease(rgb);

        CGContextDrawLinearGradient(ctx, gradient, CGPointMake(0, 0), CGPointMake(0, self.bounds.size.height), 0);
        CGGradientRelease(gradient);
    }
    else
    {
        // Do custom drawing for normal state
    }
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:@"highlighted"];

    [super dealloc];
}

@end

You may need to modify it a bit to get it to do what you want but I think you get the basic idea.

ThomasW
  • 16,981
  • 4
  • 79
  • 106
loomer
  • 2,017
  • 1
  • 17
  • 11
  • can u plz tell me what need to be done in "// Do custom drawing for highlighted state" is it the same I am doing earlier – iPhoneDev Oct 26 '10 at 13:07
  • I've added an example on how to draw a gradient. If you want to do other custom drawing you should take a look at the Quartz 2D Programming Guide. – loomer Oct 26 '10 at 13:28
  • 3
    I'll contribute by saying that if you implement the button from a storyboard/IB you should add the observer in -(void)awakeFromNib, because initWithFrame never gets called. Anyway, thanks for the answer! – tomidelucca Jun 28 '12 at 03:41
  • 1
    Why do you say that Apple may reject your app for using -insertSublayer:atIndex:? I see nothing in the docs for UIButton, UIView, or CALayer to suggest the method is deprecated. And under UIView "alternatives to subclassing", it says: "Animations are another way to make visible changes to a view without requiring you to subclass and implement complex drawing code." – c roald Sep 04 '12 at 14:09
31

Try this:

[Button addTarget:self action:@selector(doSomething:) forControlEvents:UIControlEventTouchUpInside];
[Button addTarget:self action:@selector(setBgColorForButton:) forControlEvents:UIControlEventTouchDown];
[Button addTarget:self action:@selector(clearBgColorForButton:) forControlEvents:UIControlEventTouchDragExit];

-(void)setBgColorForButton:(UIButton*)sender
{
    [sender setBackgroundColor:[UIColor blackColor]];
}

-(void)clearBgColorForButton:(UIButton*)sender
{
    [sender setBackgroundColor:[UIColor redColor]];
}

-(void)doSomething:(UIButton*)sender
{
double delayInSeconds = 0.3;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [sender setBackgroundColor:[UIColor redColor]];
    });
    //do something

}
henghonglee
  • 1,732
  • 3
  • 20
  • 29
  • thanks! that worked for me, no need to subclass the button. Great idea! – Alexander Kostiev Apr 15 '13 at 13:45
  • 1
    actually, there was one problem - when user taps the button and moves his touch out of it - button remains highlighted, but this can be easily fixed with event ...DragExit. – Alexander Kostiev Apr 19 '13 at 07:05
  • 1
    thank you. very simple solution. everyone else is overthinking this one. – roocell Apr 21 '13 at 13:41
  • 1
    Thanks for this. But for me, the highlighted state gets activated only while i leave the 'click' in the simulator. Is the behavior in actual device different? or does this work even in simulator? – Pratik Shah Sep 13 '13 at 05:14
25

You can use this method to create and solid UIImage of specified color and then apply it to your button. I've made a category for UIImage to do this.

+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size
{
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, (CGRect){.size = size});

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

+ (UIImage *)imageWithColor:(UIColor *)color
{
    return [UIImage imageWithColor:color size:CGSizeMake(1, 1)];
}

Usage:

[button setBackgroundImage:[UIImage imageWithColor:[UIColor redColor]]
                  forState:UIControlStateNormal];

As Matthias pointed out, you can safely use 1x1 image for your button background – it will be stretched to fill the button.

Community
  • 1
  • 1
Pavel Alexeev
  • 6,026
  • 4
  • 43
  • 51
  • 3
    Thanks for the idea. I replaced the UIView rendering with direct drawing the context, though. E.g.: `CGContextSetFillColorWithColor(context, color.CGColor); CGContextFillRect(context, CGRectMake(0, 0, 1, 1));` (I used 1,1 as size because I created a 1x1 image. The UIButton will stretch the image.) – Matthias Bauch Aug 08 '13 at 01:27
  • Yes, CGContextSetFillColorWithColor is much better! – Pavel Alexeev Aug 30 '13 at 13:45
  • 2
    this is probably best solution so far :D – Mapedd Nov 15 '13 at 10:43
  • This is definitely the best solution so far but does not work for round corners on a button :( – palme Jan 28 '14 at 16:18
  • 1
    From my point of view your solution is the best and more comfotable to implement. Grats. – Resty Jul 16 '14 at 11:49
  • 4
    This works for rounded corners. Just set `button.clipsToBounds = YES;` – John Slade Aug 11 '14 at 01:24
  • Great! I have been looking for the solution for rounded corners! Thx @JohnSlade – vatti May 22 '15 at 14:52
5

I found this AYUIButton on GitHub and it works perfectly :) Here is sample code:

[btn setBackgroundColor:[UIColor redColor] forState:UIControlStateNormal];
[btn setBackgroundColor:[UIColor blueColor] forState:UIControlStateHighlighted];
zvjerka24
  • 1,772
  • 1
  • 21
  • 27
2

When you set the TintColor of the Button it works when highlighted

[YourButton setTintColor:[UIColor color]];
Pach
  • 861
  • 11
  • 18
  • what do you need to do & what´s your problem ? Post some code if you need help @Vidhi have you checked the other answers ? – Pach Oct 17 '14 at 09:32
  • 1
    I wanted to change background color of `UIButton` when it is tapped / highlighted. I tried your code but it did not change background color. Therefore i ended up with solution `[btnSubmit addTarget:self action:@selector(backgroundwhite:) forControlEvents:UIControlEventTouchDown]; -(void)backgroundwhite:(id)sender { [sender setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [sender setBackgroundColor:[UIColor whiteColor]]; }` – Vidhi Oct 20 '14 at 06:19
0

Swift

Extension for UIButton that let you specify background color for each state:

https://github.com/acani/UIButtonBackgroundColor

Please note that for iphone 6 plus you should change the following, inside the extension in the .swift class, that will fix it for all the versions:

//let rect = CGRect(x: 0, y: 0, width: 0.5, height: 0.5) comment this line
    let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
Juan Boero
  • 6,281
  • 1
  • 44
  • 62