开发者

Core Data data model: attribute type for UIColor

I'm just starting out with Core 开发者_运维知识库Data and right now I'm building my data model. I need a UIColor attribute for my entity, but the type dropdown for the attribute doesn't have an option for this. Do I set it to Undefined or what?

Thanks


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.


What you probably want is a transformable attribute. Give the section on "Non-standard Persistent Attributes" in the Core Data Programming Guide an other read. A transformable attribute is, underneath the covers, a binary data attribute, but Core Data will automatically use the NSValueTransformer of your specification to serialize and unserialize the logical attribute value for you. For values that are NSCoding compliant, the NSKeyedUnarchiveFromDataTransformerName (which is the default transformer) will do the trick.

Of course, Core Data cannot index or, for an SQLite backend, query against this transformable value.


Is it possible to store the UIColor as a String as a HEX (FFFFFF, or other web-safe colors) and then when you come to read the color, you convert the string into a format that UIColor can understand?


After specify color attribute as transformable with name ColorToDataTransformer. We can simply generate MangatedObjectSubclass and write transformer code inside it

//SampleEntity.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface SampleEntity : NSManagedObject
@property (nonatomic, retain) UIColor *color;
@end

@interface ColorToDataTransformer : NSValueTransformer
@end

//SampleEntity.m

#import "SampleEntity.h"

@implementation SampleEntity

@dynamic color;

@end

@implementation ColorToDataTransformer

+ (BOOL)allowsReverseTransformation {
    return YES;
}

+ (Class)transformedValueClass {
    return [NSData class];
}


- (id)transformedValue:(id)value {
    UIColor *color = (UIColor *)value;
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
    return data;
}

- (id)reverseTransformedValue:(id)value {
    NSData *data = (NSData *)value;
    UIColor *color = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    return color;
}

@end


Here it is a Swift's Version of @RosePerrone answers. I Hope it helps.

class ColorToDataTransformer: ValueTransformer {

// 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.
override class func allowsReverseTransformation() -> Bool {
    return true
}

override class func transformedValueClass() -> AnyClass {
    return NSData.self
}

// Takes a UIColor, returns an NSData
override func transformedValue(_ value: Any?) -> Any? {
    guard let color = value as? UIColor else { return nil }

    guard let components: [CGFloat] = color.cgColor.components else { return nil }

    let colorAsString: String = String(format: "%f,%f,%f,%f", components[0], components[1], components[2], components[3])

    return colorAsString.data(using: .utf8)
}

// Takes an NSData, returns a UIColor
override func reverseTransformedValue(_ value: Any?) -> Any? {
    guard let data = value as? Data else { return nil }

    guard let colorAsString = String(data: data, encoding: .utf8) else { return nil }

    let componets: [String] = colorAsString.components(separatedBy: ",")

    var values: [Float] = []

    for component in componets {
        guard let value = Float(component) else { return nil }
        values.append(value)
    }

    let red: CGFloat = CGFloat(values[0])
    let green: CGFloat = CGFloat(values[1])
    let blue: CGFloat = CGFloat(values[2])
    let alpha: CGFloat = CGFloat(values[3])

   return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}

}

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜