Saving [UIColor colorWithPatternImage:image] UIColor to Core Data using NSKeyedArchiver
Asked Answered
M

3

2

I'm unable to create an NSData object from a UIColor (with a pattern) created with the factory method

[UIColor colorWithPatternImage:image]

works fine for standard UIColor objects. Wondering if there is another way to save a UIColor with a pattern into Core Data.

I am using the following code to archive the UIColor (with a pattern)...

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

}

and these are the errors I'm receiving...

-[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Only support RGBA or the White color space, this method is a hack.'
Mnemosyne answered 17/9, 2011 at 18:11 Comment(0)
M
5

Oh Yes! I got it. With a lot of help from the following people/posts...

Gave me the idea to use associatedObjects

Explanation of associatedObjects

and method swizzling

Create a category on UIColor. Use an Associated Object to set a reference to the pattern image in the UIColor instance (kind of like a dynamic property), don't forget to import <objc/runtime.h>. When you create your UIColor color = [UIColor colorWithPatternImage:selectedImage], also set the associated object on the color [color setAssociatedObject:selectedImage].

Then implement custom encodeWithCoder and initWithCoder methods in the category to serialize the UIImage.

And finally do some method swizzling in the main.m file so you can invoke the original UIColor encodeWithCoder and initWithCoder methods from within your UIColor Category. Then you don't even need to write your own Value Transformer for Core Data because UIColor implements the NSCoding protocol. Code below...

UIColor+patternArchive

#import "UIColor+patternArchive.h"
#import <objc/runtime.h>

@implementation UIColor (UIColor_patternArchive)

static char STRING_KEY; // global 0 initialization is fine here, no 
                        // need to change it since the value of the
                        // variable is not used, just the address

- (UIImage*)associatedObject 
{ 
    return objc_getAssociatedObject(self,&STRING_KEY); 
} 

- (void)setAssociatedObject:(UIImage*)newObject 
{ 
    objc_setAssociatedObject(self,&STRING_KEY,newObject,OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

- (void)encodeWithCoderAssociatedObject:(NSCoder *)aCoder 
{ 
    if (CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor))==kCGColorSpaceModelPattern) 
    { 
        UIImage *i = [self associatedObject]; 
        NSData *imageData = UIImagePNGRepresentation(i);
        [aCoder encodeObject:imageData forKey:@"associatedObjectKey"]; 
        self = [UIColor clearColor]; 
    } else {

        // Call default implementation, Swizzled
        [self encodeWithCoderAssociatedObject:aCoder];
    }
}

- (id)initWithCoderAssociatedObject:(NSCoder *)aDecoder 
{ 
    if([aDecoder containsValueForKey:@"associatedObjectKey"])
    { 
        NSData *imageData = [aDecoder decodeObjectForKey:@"associatedObjectKey"];
        UIImage *i = [UIImage imageWithData:imageData];
        self = [[UIColor colorWithPatternImage:i] retain]; 
        [self setAssociatedObject:i]; 
        return self; 
    } 
    else 
    { 
        // Call default implementation, Swizzled
        return [self initWithCoderAssociatedObject:aDecoder];
    } 
}

main.m

#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "UIColor+patternArchive.h"

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // Swizzle UIColor encodeWithCoder:
    Method encodeWithCoderAssociatedObject = class_getInstanceMethod([UIColor class], @selector(encodeWithCoderAssociatedObject:));
    Method encodeWithCoder = class_getInstanceMethod([UIColor class], @selector(encodeWithCoder:));
    method_exchangeImplementations(encodeWithCoder, encodeWithCoderAssociatedObject);

    // Swizzle UIColor initWithCoder:
    Method initWithCoderAssociatedObject = class_getInstanceMethod([UIColor class], @selector(initWithCoderAssociatedObject:));
    Method initWithCoder = class_getInstanceMethod([UIColor class], @selector(initWithCoder:));
    method_exchangeImplementations(initWithCoder, initWithCoderAssociatedObject);

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}
Mnemosyne answered 19/9, 2011 at 21:23 Comment(0)
W
1

Since UIColor implements the NSCoding protocol, you don't have to write your own transformer but can just tell Core Data to use the NSKeyedUnarchiveFromDataTransformerName transform (which is the default.)

To my knowledge, that transform handles patterns in UIColor objects.

Wed answered 18/9, 2011 at 14:30 Comment(1)
Still not working, interesting though that Core Data will use the default value transformer when you don't specify one. It works for a standard UIColor object but still getting same errors for a UIColor object with a pattern.Mnemosyne
C
0

Im sure theres a way to convert uicolor into hex strings, you could simply store the hex string has "NSString" in core data model.

How can I convert RGB hex string into UIColor in objective-c?

Checkers answered 21/5, 2012 at 2:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.