30

I have a UIButton with an image and on its disabled state, this image should have .3 alpha.

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *arrowImage = [UIImage imageNamed:@"arrow.png"];
[button setImage:arrowImage forState:UIControlStateNormal];

// The arrow should be half transparent here
[button setImage:arrowImage forState:UIControlStateDisabled];

How do I accomplish this?

UPDATE: I noticed, by default UIButton does reduce the alpha of the image on disabled state (probably at .5?). But I'd like to know how to fine-tune this value.

Irfan
  • 4,301
  • 6
  • 29
  • 46
pixelfreak
  • 17,714
  • 12
  • 90
  • 109

11 Answers11

20

If setting alpha while the button is disabled doesn't work, then just make your disabled image at the alpha value you desire.

Just tested this, you can set the alpha on the UIButton, regardless of state and it works just fine.

self.yourButton.alpha = 0.25;
EricLeaf
  • 892
  • 5
  • 12
  • The problem is the prepared image doesn't know what view is going to be behind it (which is the point of alpha) – bryanmac Sep 25 '11 at 03:22
  • I agree it is the point of alpha, PNG supports alpha, so now whats the problem with using a stock image with alpha? I do this all the time, in fact the only time I don't use it is when I want to dynamically change alpha for a fade effect at runtime. – EricLeaf Sep 25 '11 at 05:46
  • 1
    Good point on the alpha PNG. Also for [button setAlpha], I saw that and it works, the only problem is you or other code can't simply set the state of the button - you always have to remember to set the alpha and reset the alpha on state changes. – bryanmac Sep 25 '11 at 12:42
15

Credit goes to @bryanmac for his helpful answer. I used his code as a starting point, but found the same thing can be achieved without using a UIImageView.

Here's my solution:

- (UIImage *)translucentImageFromImage:(UIImage *)image withAlpha:(CGFloat)alpha
{
    CGRect rect = CGRectZero;
    rect.size = image.size;

    UIGraphicsBeginImageContext(image.size);
    [image drawInRect:rect blendMode:kCGBlendModeScreen alpha:alpha];
    UIImage * translucentImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return translucentImage;
}

Set the button's background image for disabled state:

UIImage * disabledBgImage = [self translucentImageFromImage:originalBgImage withAlpha:0.5f];
[button setBackgroundImage:disabledBgImage forState:UIControlStateDisabled];

EDIT:

I refined my solution further by creating a category on UIImage with this method:

- (UIImage *)translucentImageWithAlpha:(CGFloat)alpha
{
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
    [self drawInRect:bounds blendMode:kCGBlendModeScreen alpha:alpha];

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

    return translucentImage;
}

Set the button's background image for disabled state:

UIImage * disabledBgImage = [originalBgImage translucentImageWithAlpha:0.5f];
[button setBackgroundImage:disabledBgImage forState:UIControlStateDisabled];
Community
  • 1
  • 1
Steph Sharp
  • 11,462
  • 5
  • 44
  • 81
  • 4
    This worked great. However it didn't use the @2x version of my image. If you use UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0 ); instead of UIGraphicsBeginImageContext(image.size) it will support retina images too. – Jon Hobson Mar 25 '14 at 11:36
  • This is a great answer! I am now using this in my app. Thanks @StephSharp! – Fabien Warniez Mar 15 '15 at 05:38
7

You need two instances of UIImage, one for enabled and one for disabled.

The tough part is for the disabled one, you can't set alpha on UIImage. You need to set it on UIImageView but button doesn't take an UIImageView, it takes a UIImage.

If you really want to do this, you can load the same image into the disabled button state after creating a resultant image from the UIImageView that has the alpha set on it.

UIImageView *uiv = [UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrow.png"];

// get resultant UIImage from UIImageView
UIGraphicsBeginImageContext(uiv.image.size);
CGRect rect = CGRectMake(0, 0, uiv.image.size.width, uiv.image.size.height);
[uiv.image drawInRect:rect blendMode:kCGBlendModeScreen alpha:0.2];  

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

[button setImage:disabledArrow forState:UIControlStateDisabled];

That's a lot to go through to get an alpha controlled button image. There might be an easier way but that's all I could find. Hope that helps.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
bryanmac
  • 38,941
  • 11
  • 91
  • 99
5

Subclassing UIButton and extending the setEnabled: method seems to work:

- (void) setEnabled:(BOOL)enabled {    
    [super setEnabled:enabled];
    if (enabled) {
        self.imageView.alpha = 1;
    } else {
        self.imageView.alpha = .25;
    }
}

I haven't tested it thoroughly, but did find the value resets if the screen is rotated. Adding the same code to the setFrame: method fixes that, but I'm not sure if there are other situations where this value is changed.

Anthony Mattox
  • 7,048
  • 6
  • 43
  • 59
  • 1
    I've found that sometimes this doesn't work. I think UIButton modifies `imageView` when doing its own stuff under the hood (perhaps changing between default, selected images?), and the alpha value may not persist if `setEnabled` is being called as the button is tapped (e.g., calling `setEnabled` while the UIButton is simultaneously modifying its appearance between selected / highlighted / default states). – Tim Arnold Dec 17 '12 at 17:33
  • Then override 'layoutSubviews' and call '[self setNeedsLayout]' when needed. – Cyrille Feb 28 '14 at 08:26
  • `-(void)setEnabled:(BOOL)enabled { super.enabled = enabled; self.imageView.alpha = enabled ? 1 : 0.5; self.titleLabel.alpha = self.imageView.alpha; }` – Peter DeWeese Aug 18 '15 at 20:19
5

In case anyone needs it, here's an answer in Swift:

Subclass UIButton and override enabled

class MyCustomButton: UIButton {

override var enabled: Bool {
    didSet{
        alpha = enabled ? 1.0 : 0.3
    }
}

Set the class of any button you want to have this property to "MyCustomButton" (or whatever you choose to name it)

 @IBOutlet weak var nextButton: MyCustomButton!
Kay
  • 96
  • 2
  • 4
2

I found that none of these solutions really work. The cleanest way of doing it is to subclass, and instead of using self.imageView, just add your own custom imageView like this:

_customImageView = [[UIImageview alloc] initWithImage:image];

Then add your custom alpha like so:

- (void)setEnabled:(BOOL)enabled {
    [super setEnabled:enabled];
    if (enabled) {
        _customImageView.alpha = 1.0f;
    } else {
        _customImageView.alpha = 0.25f;
    }
}
JPN
  • 632
  • 12
  • 24
2

Swift 3:

extension UIImage {
    func copy(alpha: CGFloat) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        draw(at: CGPoint.zero, blendMode: .normal, alpha: alpha)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
}

Usage:

button.setImage(image.copy(alpha: 0.3), for: .disabled)
sash
  • 8,423
  • 5
  • 63
  • 74
1

The button’s image view. (read-only)

@property(nonatomic, readonly, retain) UIImageView *imageView

Although this property is read-only, its own properties are read/write.

Luke
  • 11,426
  • 43
  • 60
  • 69
dahe
  • 17
  • 1
1

A swift 4 version of Steph Sharp's answer:

extension UIImage {

    func translucentImageWithAlpha(alpha: CGFloat) -> UIImage {

        UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0)
        let bounds = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
        self.draw(in: bounds, blendMode: .screen, alpha: alpha)

        let translucentImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return translucentImage!
    }
}

Which you can use like this:

if let image = self.image(for: .normal) {
    self.setImage(image.translucentImageWithAlpha(alpha: 0.3), for: .disabled)
}
Pochi
  • 13,391
  • 3
  • 64
  • 104
0
- (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; }

Use: [btnRegister setBackgroundImage:[self imageWithColor:[UIColor lightGrayColor]] forState:UIControlStateDisabled]; btnRegister.enabled = false;

RAUL QUISPE
  • 800
  • 9
  • 13
  • This is a code dump with no helpful attempt at commenting on anything. Please improve this. – Drew Jan 06 '16 at 07:38
0

Here's a solution that extends the UIButton class. No need to sub-class. In my example, button alpha is set to 0.55 if .isEnabled is false, and 1.0 if it's true.

Swift 5:

extension UIButton {
    override open var isEnabled: Bool {
        didSet {
            self.alpha = isEnabled ? 1.0 : 0.55
        }
    }
}
Sohil R. Memon
  • 9,404
  • 1
  • 31
  • 57
theogood
  • 85
  • 1
  • 1
  • 7