I ran into a situation yesterday where I needed to lighten a UIColor so I created a category and added a lighten
method. I thought it would be straight forward to just multiply the value by each component of the color but my greens started turning yellow so I knew it had to be more complicated.
The solution I came up with was to convert from sRGB to Linear, multiply the color, and then convert back. This seems to work but I'm not sure if it's "correct". I couldn't find anything in the docs that stated that UIColor was in sRGB space. I'm also no color scientist so I only have a rudimentary knowledge of the math involved.
Anyway here is my code, I'm asking for some peer review and to see if anyone has a better understanding of modifying UIColors.
CGFloat sRGB2Linear(CGFloat x){
CGFloat a = 0.055;
if(x <= 0.04045){
return x * (1.0 / 12.92);
}else{
return pow((x + a) * (1.0 / (1 + a)), 2.4);
}
}
CGFloat linear2sRGB(CGFloat x){
CGFloat a = 0.055;
if(x <= 0.0031308){
return x * 12.92;
}else{
return (1 + a) * pow(x, 1 / 2.4) - a;
}
}
- (UIColor *)lighten:(CGFloat)value{
const CGFloat *components = CGColorGetComponents([self CGColor]);
CGFloat newR = (sRGB2Linear(components[0])+1)*value;
CGFloat newG = (sRGB2Linear(components[1])+1)*value;
CGFloat newB = (sRGB2Linear(components[2])+1)*value;
newR = MAX(0, MIN(1, linear2sRGB(newR)));
newG = MAX(0, MIN(1, linear2sRGB(newG)));
newB = MAX(0, MIN(1, linear2sRGB(newB)));
return [UIColor colorWithRed:newR green:newG blue:newB alpha:components[3]];
}
In Objective-C, we use this code to set RGB color codes for views: #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] view.
The first step is to extract red, green, blue, and alpha components from the current UIColor . Then, to each color component, add a componentDelta to make the color lighter or darker. Each color component value is between 0 and 1 .
This is my iOS5+ solution to make relative brightness changes easily (lighten and darken all-in-one):
+ (UIColor*)changeBrightness:(UIColor*)color amount:(CGFloat)amount
{
CGFloat hue, saturation, brightness, alpha;
if ([color getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]) {
brightness += (amount-1.0);
brightness = MAX(MIN(brightness, 1.0), 0.0);
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
}
CGFloat white;
if ([color getWhite:&white alpha:&alpha]) {
white += (amount-1.0);
white = MAX(MIN(white, 1.0), 0.0);
return [UIColor colorWithWhite:white alpha:alpha];
}
return nil;
}
It's invoked like so:
[self changeBrightness:someUiColor amount:1.1]
Using 1.1 it will increase the brightness by 10%; 0.9 will decrease the brightness by 10%. Note that the 10% is relative to pure white (i.e., 10% is always a .1 increase in brightness.) This is the expected behavior if you want a percentage increase to lighten or darken the colors consistently regardless of their initial brightness.
You can work with hue, saturation, and brightness.
Get the old values using
- (BOOL)getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha
then adjust the brightness, and construct a new color:
- (UIColor *)initWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha
Keep in mind that you need to pass pointers to the first function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With