Get RGB value from UIColor presets

I my application I pass RGB color value to server. My app uses UIColor predefined values, like [UIColor grayColor], [UIColor redColor]. I know that I can use following code:

const CGFloat *c = CGColorGetComponents(color.CGColor)

but only for colors that are in RBG color space, however, [UIColor grayColor] is not.

Is there any way to get RGB values for non-RBG colors?

Thanks!


UIColor has a method which gives you the RGB components (-getRed:green:blue:alpha:) which works great on iOS 7 or higher. On iOS 6 and earlier, this method will fail and return NO if the color is not in an RGB color space (as it will for [UIColor grayColor].)

For iOS 6 and earlier, the only way I know of for doing this that works in all color spaces is to create a Core Graphics bitmap context in an RGB color space and draw into it with your color. You can then read out the RGB values from the resulting bitmap. Note that this won't work for certain colors, like pattern colors (eg. [UIColor groupTableViewBackgroundColor]), which don't have reasonable RGB values.

- (void)getRGBComponents:(CGFloat [3])components forColor:(UIColor *)color {
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char resultingPixel[4];
    CGContextRef context = CGBitmapContextCreate(&resultingPixel,
                                                 1,
                                                 1,
                                                 8,
                                                 4,
                                                 rgbColorSpace,
                                                 kCGImageAlphaNoneSkipLast);
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
    CGContextRelease(context);
    CGColorSpaceRelease(rgbColorSpace);

    for (int component = 0; component < 3; component++) {
        components[component] = resultingPixel[component] / 255.0f;
    }
}

You can use it something like this:

    CGFloat components[3];
    [self getRGBComponents:components forColor:[UIColor grayColor]];
    NSLog(@"%f %f %f", components[0], components[1], components[2]);

I only know of one correct way to do this — and that is to call CGGetColorComponents(). I have tried using UIColor's -getRed:green:blue:alpha, but it does not work correctly on grayscale colors. So here is what I do:

First, I have a simple struct that lets me manipulate RGBA colors easily. This structure is useful elsewhere for doing all sorts of color manipulation stuff like gamma correction and color averaging.

typedef struct
{
  CGFloat r;  // Red component (0 <= r <= 1)
  CGFloat g;  // Green component (0 <= g <= 1)
  CGFloat b;  // Blue component (0 <= b <= 1)
  CGFloat a;  // Alpha/opacity component (0 <= a <= 1)
}
RGBA;

Then, I have a function that returns an RGBA structure given a UIColor.

RGBA RGBAFromUIColor(UIColor *color)
{
  return RGBAFromCGColor(color.CGColor);
}

It passes the UIColor's CGColor to a lower-level function to do the heavy lifting.

RGBA RGBAFromCGColor(CGColorRef color)
{
  RGBA rgba;

  CGColorSpaceRef color_space = CGColorGetColorSpace(color);
  CGColorSpaceModel color_space_model = CGColorSpaceGetModel(color_space);
  const CGFloat *color_components = CGColorGetComponents(color);
  int color_component_count = CGColorGetNumberOfComponents(color);

  switch (color_space_model)
  {
    case kCGColorSpaceModelMonochrome:
    {
      assert(color_component_count == 2);
      rgba = (RGBA)
      {
        .r = color_components[0],
        .g = color_components[0],
        .b = color_components[0],
        .a = color_components[1]
      };
      break;
    }

    case kCGColorSpaceModelRGB:
    {
      assert(color_component_count == 4);
      rgba = (RGBA)
      {
        .r = color_components[0],
        .g = color_components[1],
        .b = color_components[2],
        .a = color_components[3]
      };
      break;
    }

    default:
    {
      NSLog(@"Unsupported color space model %i", color_space_model);
      rgba = (RGBA) { 0, 0, 0, 0 };
      break;
    }
  }

  return rgba;
}

So, this works great for grayscale colors (like UIColor.whiteColor, UIColor.blackColor, UIColor.grayColor, etc.) as well as any red/green/blue/alpha color. It won't work with HSB colors, however.


Adding to Jesse's answer, here is how to preserve alpha:

- (void)getRGBAComponents:(CGFloat [4])components forColor:(UIColor *)color {
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char resultingPixel[4] = {0};
    CGContextRef context = CGBitmapContextCreate(&resultingPixel,
                                                 1,
                                                 1,
                                                 8,
                                                 4,
                                                 rgbColorSpace,
                                                 kCGImageAlphaPremultipliedLast);
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
    CGContextRelease(context);
    CGColorSpaceRelease(rgbColorSpace);

    CGFloat a = resultingPixel[3] / 255.0;
    CGFloat unpremultiply = (a != 0.0) ? 1.0 / a / 255.0 : 0.0;
    for (int component = 0; component < 3; component++) {
        components[component] = resultingPixel[component] * unpremultiply;
    }
    components[3] = a;
}