Creating a UIImage from a UIColor to use as a background image for UIButton [duplicate]

I'm creating a colored image like this:

CGRect rect = CGRectMake(0, 0, 1, 1);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context,
                                   [[UIColor redColor] CGColor]);
//  [[UIColor colorWithRed:222./255 green:227./255 blue: 229./255 alpha:1] CGColor]) ;
CGContextFillRect(context, rect);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

and then use it as the background image of a button:

[resultButton setBackgroundImage:img forState:UIControlStateNormal];

This works great using the redColor, however I want to use an RGB color (as shown in the commented line). When I use the RGB color, the buttons background isn't changed.

What am I missing?


Solution 1:

I created a category around UIButton to be able to set the background color of the button and set the state. You might find this useful.

@implementation  UIButton (ButtonMagic)

- (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state {
    [self setBackgroundImage:[UIButton imageFromColor:backgroundColor] forState:state];
}

+ (UIImage *)imageFromColor:(UIColor *)color {
    CGRect rect = CGRectMake(0, 0, 1, 1);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

This will be part of a set of helper categories I'm open sourcing this month.

Swift 2.2

extension UIImage {
static func fromColor(color: UIColor) -> UIImage {
    let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()
    CGContextSetFillColorWithColor(context, color.CGColor)
    CGContextFillRect(context, rect)
    let img = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return img
  }
}

Swift 3.0

extension UIImage {
    static func from(color: UIColor) -> UIImage {
        let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        context!.setFillColor(color.cgColor)
        context!.fill(rect)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }
}

Use as

let img = UIImage.from(color: .black)

Solution 2:

Xamarin.iOS solution

 public UIImage CreateImageFromColor()
 {
     var imageSize = new CGSize(30, 30);
     var imageSizeRectF = new CGRect(0, 0, 30, 30);
     UIGraphics.BeginImageContextWithOptions(imageSize, false, 0);
     var context = UIGraphics.GetCurrentContext();
     var red = new CGColor(255, 0, 0);
     context.SetFillColor(red);
     context.FillRect(imageSizeRectF);
     var image = UIGraphics.GetImageFromCurrentImageContext();
     UIGraphics.EndImageContext();
     return image;
 }

Solution 3:

Are you using ARC? The problem here is that you don't have a reference to the UIColor object that you're trying to apply; the CGColor is backed by that UIColor, but ARC will deallocate the UIColor before you have a chance to use the CGColor.

The clearest solution is to have a local variable pointing to your UIColor with the objc_precise_lifetime attribute.

You can read more about this exact case on this article about UIColor and short ARC lifetimes or get more details about the objc_precise_lifetime attribute.