How to change the background color of a UIButton w

2019-01-01 09:53发布

At some point in my app I have a highlighted UIButton (for example when a user has his finger on the button) and I need to change the background color while the button is highlighted (so while the finger of the user is still on the button).

I tried the following:

_button.backgroundColor = [UIColor redColor];

But it is not working. The color remains the same. I tried the same piece of code when the button is not highlighted and it works fine. I also tried calling -setNeedsDisplay after changing the color, it didn't have any effect.

How to force the button to change the background color?

26条回答
爱死公子算了
2楼-- · 2019-01-01 10:25

Not sure if this sort of solves what you're after, or fits with your general development landscape but the first thing I would try would be to change the background colour of the button on the touchDown event.

Option 1:

You would need two events to be capture, UIControlEventTouchDown would be for when the user presses the button. UIControlEventTouchUpInside and UIControlEventTouchUpOutside will be for when they release the button to return it to the normal state

UIButton *myButton =  [UIButton buttonWithType:UIButtonTypeCustom];
[myButton setFrame:CGRectMake(10.0f, 10.0f, 100.0f, 20.f)];
[myButton setBackgroundColor:[UIColor blueColor]];
[myButton setTitle:@"click me:" forState:UIControlStateNormal];
[myButton setTitle:@"changed" forState:UIControlStateHighlighted];
[myButton addTarget:self action:@selector(buttonHighlight:) forControlEvents:UIControlEventTouchDown];
[myButton addTarget:self action:@selector(buttonNormal:) forControlEvents:UIControlEventTouchUpInside];

Option 2:

Return an image made from the highlight colour you want. This could also be a category.

+ (UIImage *)imageWithColor:(UIColor *)color {
   CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
   UIGraphicsBeginImageContext(rect.size);
   CGContextRef context = UIGraphicsGetCurrentContext();

   CGContextSetFillColorWithColor(context, [color CGColor]);
   CGContextFillRect(context, rect);

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

   return image;
}

and then change the highlighted state of the button:

[myButton setBackgroundImage:[self imageWithColor:[UIColor greenColor]] forState:UIControlStateHighlighted];
查看更多
人间绝色
3楼-- · 2019-01-01 10:25
class CustomButton: UIButton {

    override var isHighlighted: Bool {
        didSet {
            if (isHighlighted) {
                alpha = 0.5
            }
            else {
                alpha = 1
            }            
        }
    }

}
查看更多
爱死公子算了
4楼-- · 2019-01-01 10:27

You can subclass the UIButton and make a nice forState.

colourButton.h

#import <UIKit/UIKit.h>

@interface colourButton : UIButton

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

@end

colourButton.m

#import "colourButton.h"

@implementation colourButton
{
    NSMutableDictionary *colours;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];

    // If colours does not exist
    if(!colours)
    {
        colours = [NSMutableDictionary new];  // The dictionary is used to store the colour, the key is a text version of the ENUM
        colours[[NSString stringWithFormat:@"%lu", UIControlStateNormal]] = (UIColor*)self.backgroundColor;  // Store the original background colour
    }

    return self;
}

-(void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state
{
    // If it is normal then set the standard background here
    if(state & UIControlStateNormal)
    {
        [super setBackgroundColor:backgroundColor];
    }

    // Store the background colour for that state
    colours[[NSString stringWithFormat:@"%lu", state]]= backgroundColor;
}

-(void)setHighlighted:(BOOL)highlighted
{
    // Do original Highlight
    [super setHighlighted:highlighted];

    // Highlight with new colour OR replace with orignial
    if (highlighted && colours[[NSString stringWithFormat:@"%lu", UIControlStateHighlighted]])
    {
        self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateHighlighted]];
    }
    else
    {
        self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateNormal]];
    }
}

-(void)setSelected:(BOOL)selected
{
    // Do original Selected
    [super setSelected:selected];

    // Select with new colour OR replace with orignial
    if (selected && colours[[NSString stringWithFormat:@"%lu", UIControlStateSelected]])
    {
        self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateSelected]];
    }
    else
    {
        self.backgroundColor = colours[[NSString stringWithFormat:@"%lu", UIControlStateNormal]];
    }
}

@end

Notes (This is an example, I know there are problems and here are some)

I have used an NSMutableDictionay to store the UIColor for each State, I have to do a nasty text conversion for the Key as the UIControlState is not a nice straight Int. If it where you could init an Array with that many objects and use the State as an index.

Because of this you many have difficulties with e.g. a selected & disabled button, some more logic is needed.

Another problem is if you try and set multiple colours at the same time, I have not tried with a button but if you can do this it may not work

 [btn setBackgroundColor:colour forState:UIControlStateSelected & UIControlStateHighlighted];

I have assumed this is StoryBoard, there is no init, initWithFrame so add them if you need them.

查看更多
倾城一夜雪
5楼-- · 2019-01-01 10:28

Subclass the UIButton and add inspectable properties for convenient use (written in Swift 3.0):

final class SelectableBackgroundButton: UIButton {

    private struct Constants {
        static let animationDuration: NSTimeInterval = 0.1
    }

    @IBInspectable
    var animatedColorChange: Bool = true

    @IBInspectable
    var selectedBgColor: UIColor = UIColor.blackColor().colorWithAlphaComponent(0.2)

    @IBInspectable
    var normalBgColor: UIColor = UIColor.clearColor()

    override var selected: Bool {
        didSet {
            if animatedColorChange {
                UIView.animateWithDuration(Constants.animationDuration) {
                    self.backgroundColor = self.selected ? self.selectedBgColor : self.normalBgColor
                }
            } else {
                self.backgroundColor = selected ? selectedBgColor : normalBgColor
            }
        }
    }

    override var highlighted: Bool {
        didSet {
            if animatedColorChange {
                UIView.animateWithDuration(Constants.animationDuration) {
                    self.backgroundColor = self.highlighted ? self.selectedBgColor : self.normalBgColor
                }
            } else {
                self.backgroundColor = highlighted ? selectedBgColor : normalBgColor
            }
        }
    }
}
查看更多
时光乱了年华
6楼-- · 2019-01-01 10:29

You can override UIButton's setHighlighted method.

Objective-C

- (void)setHighlighted:(BOOL)highlighted {
    [super setHighlighted:highlighted];

    if (highlighted) {
        self.backgroundColor = UIColorFromRGB(0x387038);
    } else {
        self.backgroundColor = UIColorFromRGB(0x5bb75b);
    }
}

Swift 3.0 and Swift 4.1

override open var isHighlighted: Bool {
    didSet {
        backgroundColor = isHighlighted ? UIColor.black : UIColor.white
    }
}
查看更多
回忆,回不去的记忆
7楼-- · 2019-01-01 10:29

a more compact solution (based on @aleksejs-mjaliks answer):

Swift 3/4+:

override var isHighlighted: Bool {
    didSet {
        backgroundColor = isHighlighted ? .lightGray : .white
    }
}

Swift 2:

override var highlighted: Bool {
    didSet {
        backgroundColor = highlighted ? UIColor.lightGrayColor() : UIColor.whiteColor()
    }
}

If you don't want to override, this is an updated version of @timur-bernikowich's answer (Swift 4.2):

extension UIButton {
  func setBackgroundColor(_ color: UIColor, forState controlState: UIControl.State) {
    let colorImage = UIGraphicsImageRenderer(size: CGSize(width: 1, height: 1)).image { _ in
      color.setFill()
      UIBezierPath(rect: CGRect(x: 0, y: 0, width: 1, height: 1)).fill()
    }
    setBackgroundImage(colorImage, for: controlState)
  }
}
查看更多
登录 后发表回答