I'll paraphrase the definitve answer I found in More iPhone 3 Development by Dave Mark and Jeff LeMarche:
Usually we'd be able to leave the transformable attribute's transformer class as the default, NSKeyedUnarchiveFromData, and be done, but in this case we can't because UIColor
doesn't conform to NSCoding
and can't be archived using an NSKeyedArchiver
. We have to manually write a value transformer to handle the transformation.
Add an attribute to your entity and call the attribute "color", or whatever name you wish. Set its type to Transformable. Set its "Value Transformer Name" to UIColorRGBValueTransformer. Note that the data model editor doesn't validate the Value Transformer Name: to make sure it's a valid class, so type carefully.
Create a new file, a subclass of NSObject
, and name it UIColorRGBValueTransformer.m.
Click UIColorRGBValueTransformer.h and change the superclass from NSObject to NSValueTransformer. Also, change #import <Foundation/Foundation.h>
to #import <UIKit/UIKit.h>
, since UIColor
is part of UIKit
, not Foundation
.
Now in UIColorRGBValueTransformer.m, we need to implement four methods that allow our value transformer class to convert instances of UIColor
to NSData
and vice versa. Include the following code in UIColorRGBValueTransformer.m:
#import "UIColorRGBValueTransformer.h"
@implementation UIColorRGBValueTransformer
// Here we override the method that returns the class of objects that this transformer can convert.
+ (Class)transformedValueClass {
return [NSData class];
}
// Here we indicate that our converter supports two-way conversions.
// That is, we need to convert UICOLOR to an instance of NSData and back from an instance of NSData to an instance of UIColor.
// Otherwise, we wouldn't be able to beth save and retrieve values from the persistent store.
+ (BOOL)allowsReversTransformation {
return YES;
}
// Takes a UIColor, returns an NSData
- (id)transfomedValue:(id)value {
UIColor *color = value;
const CGFloat *components = CGColorGetComponents(color.CGColor);
NSString *colorAsString = [NSString stringWithFormat:@"%f,%f,%f,%f", components[0], components[1], components[2], components[3]];
return [colorAsString dataUsingEncoding:NSUTF8StringEncoding];
}
// Takes an NSData, returns a UIColor
- (id)reverseTransformedValue:(id)value {
NSString *colorAsString = [[[NSString alloc] initWithData:value encoding:NSUTF8StringEncoding] autorelease];
NSArray *components = [colorAsString componentsSeparatedByString:@","];
CGFloat r = [[components objectAtIndex:0] floatValue];
CGFloat g = [[components objectAtIndex:1] floatValue];
CGFloat b = [[components objectAtIndex:2] floatValue];
CGFloat a = [[components objectAtIndex:3] floatValue];
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
@end
Now in another file, you can include a line of code like:
[self.managedObject setValue:color forKey:self.keyPath];
without needing to import UIColorRGBValueTransformer.h in the file.