iOS Color Mixing Class

Para variar a cor entre 2 ou mais cores de acordo com um valor, recomendamos a seguinte classe ColourMixer.

Fornecemos uma matriz de limites e cores e, em seguida, consultamos um valor para encontrar a cor necessária.

Esta classe suporta 2 métodos de mistura diferentes – discreto, onde as saídas não são misturadas, ou gradiente onde ocorre a mistura.

ColourMixer.h

typedef enum {
ColourMixerGradient,
ColourMixerDiscrete
} ColourMixerType;

@interface ColourMixer : NSObject

@property (nonatomic, retain) NSArray *colours;
@property (nonatomic, retain) NSArray *boundaries;
@property (nonatomic, assign) ColourMixerType type;

- (UIColor *)colourForValue:(id)value;

@end

ColourMixer.m

#import "ColourMixer.h"

@implementation ColourMixer

@synthesize colours = _colours;
@synthesize boundaries = _boundaries;
@synthesize type = _type;


- (id)init {
self = [super init];
if(self) {
[self setDefaults];
}
return self;
}

-(void)dealloc
{
[_colours release], _colours = nil;
[_boundaries release], _boundaries = nil;
[super dealloc];
}

-(void)setDefaults
{
self.type = ColourMixerDiscrete;
self.colours = [[[NSArray alloc] initWithObjects:[UIColor redColor], [UIColor orangeColor], [UIColor greenColor], nil] autorelease];
self.boundaries = [[[NSArray alloc] initWithObjects:[NSNumber numberWithFloat:0.5], [NSNumber numberWithFloat:0.8], nil] autorelease];
}

- (UIColor *)colourForValue:(id)value
{
if(self.type == ColourMixerDiscrete) {
if (self.boundaries.count != (self.colours.count - 1)) {
return nil;
}
for (int i=0; i < self.boundaries.count; i++) {
if([value compare:[self.boundaries objectAtIndex:i]] == NSOrderedAscending) {
return [self.colours objectAtIndex:i];
}
}
return [self.colours lastObject];
} else {
// Must be a continuous gradient
if (self.boundaries.count != self.colours.count) {
return nil;
}
// If lower than first boundary then return first colour
if ([value compare:[self.boundaries objectAtIndex:0]] == NSOrderedAscending) {
return [self.colours objectAtIndex:0];
}
// If bigger than last boundary then return last colour
if ([value compare:[self.boundaries lastObject]] == NSOrderedDescending) {
return [self.colours lastObject];
}
// Otherwise, need to return a mixed colour
for (int i=1; i < self.boundaries.count; i++) {
if([value compare:[self.boundaries objectAtIndex:i]] == NSOrderedAscending) {
// So the mix should be between the (i-1)th and ith colours
double range = [[self.boundaries objectAtIndex:i] doubleValue] - [[self.boundaries objectAtIndex:(i-1)] doubleValue];
double ratio = ([value doubleValue] - [[self.boundaries objectAtIndex:(i-1)] doubleValue]) / range;
return [self mixColour:[self.colours objectAtIndex:(i-1)] andColour:[self.colours objectAtIndex:i] withRatio:ratio];
}
}
// Should never be able to get here...
return nil;
}
}

- (UIColor *)mixColour:(UIColor*)firstColour andColour:(UIColor*)secondColour withRatio:(double)ratio
{
if(ratio <= 0)
return firstColour;
if(ratio >= 1)
return secondColour;

/* There is a nicer way to do this in iOS > 5, but for backwards compatibility we do it this way */
CGColorRef colorref = [firstColour CGColor];
const CGFloat *components1 = CGColorGetComponents(colorref);
CGFloat r1 = components1[0];
CGFloat g1 = components1[1];
CGFloat b1 = components1[2];
CGFloat a1 = components1[3];

colorref
= [secondColour CGColor];
const CGFloat *components2 = CGColorGetComponents(colorref);
CGFloat r2 = components2[0];
CGFloat g2 = components2[1];
CGFloat b2 = components2[2];
CGFloat a2 = components2[3];

CGFloat r3 = (r2 - r1) * ratio + r1;
CGFloat g3 = (g2 - g1) * ratio + g1;
CGFloat b3 = (b2 - b1) * ratio + b1;
CGFloat a3 = (a2 - a1) * ratio + a1;

return [UIColor colorWithRed:r3 green:g3 blue:b3 alpha:a3];
}

@end