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