Prior to iOS 5.1 if you wanted to use NSCoding protocols with UIImage you had to do something like this.
@interface UIImage (NSCoding)
-(id)initWithCoder:(NSCoder *)deocder;
-(void)encodeWithCoder:(NSCoder *)encoder;
@end
Then implement it yourself. However with iOS 5.1 this generates a warning "Category is implementing a method which will also be implemented by its primary class" in the .M file for this protocol. Now if I remove this extension class iOS5.1 will be happy, however this should crash and burn on iOS4.0. So what is the best course of action?
I have the same issue, and since it is too late to use subclass rather than cateogry, I followed zoul's suggestion to use class_addMethod, and below is my implementation:
#import "UIImage-NSCoding.h"
#include <objc/runtime.h>
#define kEncodingKey @"UIImage"
static void __attribute__((constructor)) initialize() {
@autoreleasepool {
if (![[UIImage class] conformsToProtocol:@protocol(NSCoding)]) {
Class class = [UIImage class];
if (!class_addMethod(
class,
@selector(initWithCoder:),
class_getMethodImplementation(class, @selector(initWithCoderForArchiver:)),
protocol_getMethodDescription(@protocol(NSCoding), @selector(initWithCoder:), YES, YES).types
)) {
NSLog(@"Critical Error - [UIImage initWithCoder:] not defined.");
}
if (!class_addMethod(
class,
@selector(encodeWithCoder:),
class_getMethodImplementation(class, @selector(encodeWithCoderForArchiver:)),
protocol_getMethodDescription(@protocol(NSCoding), @selector(encodeWithCoder:), YES, YES).types
)) {
NSLog(@"Critical Error - [UIImage encodeWithCoder:] not defined.");
}
}
}
}
@implementation UIImage(NSCoding)
- (id) initWithCoderForArchiver:(NSCoder *)decoder {
if ((self = [super init]))
{
NSData *data = [decoder decodeObjectForKey:kEncodingKey];
self = [self initWithData:data];
}
return self;
}
- (void) encodeWithCoderForArchiver:(NSCoder *)encoder {
NSData *data = UIImagePNGRepresentation(self);
[encoder encodeObject:data forKey:kEncodingKey];
}
@end
So far I have not noticed any further issue. Hope it helps!
[ATTENTION]
If you use this UIImage category in order to archive UIImageViewer objects, beware that NSCoding implementation of UIImageViewer in iOS 5 seems to be broken. The image property of the UIImageViewer is lost after save and load, when it is specified in XIB (I have not tried to see if there is the same issue when UIImageViewer object is being created in code). This has been fixed in iOS 6.
[UPDATES]
I changed my code to add methods in +load instead initialize(), it is still being executed only once, but earlier. My current implementation:
#import "UIImage+NSCoding.h"
#import <objc/runtime.h>
#define kEncodingKey @"UIImage"
@implementation UIImage (NSCoding)
+ (void) load
{
@autoreleasepool {
if (![UIImage conformsToProtocol:@protocol(NSCoding)]) {
Class class = [UIImage class];
if (!class_addMethod(
class,
@selector(initWithCoder:),
class_getMethodImplementation(class, @selector(initWithCoderForArchiver:)),
protocol_getMethodDescription(@protocol(NSCoding), @selector(initWithCoder:), YES, YES).types
)) {
NSLog(@"Critical Error - [UIImage initWithCoder:] not defined.");
}
if (!class_addMethod(
class,
@selector(encodeWithCoder:),
class_getMethodImplementation(class, @selector(encodeWithCoderForArchiver:)),
protocol_getMethodDescription(@protocol(NSCoding), @selector(encodeWithCoder:), YES, YES).types
)) {
NSLog(@"Critical Error - [UIImage encodeWithCoder:] not defined.");
}
}
}
}
- (id) initWithCoderForArchiver:(NSCoder *)decoder {
if (self = [super init]) {
NSData *data = [decoder decodeObjectForKey:kEncodingKey];
self = [self initWithData:data];
}
return self;
}
- (void) encodeWithCoderForArchiver:(NSCoder *)encoder {
NSData *data = UIImagePNGRepresentation(self);
[encoder encodeObject:data forKey:kEncodingKey];
}
@end
Instead of using a category on UIImage, wouldn't it be cleaner to subclass it?
Then you could implement initWithCoder
and encodeWithCoder
and use UIImage's NSCoding implementation on 5.1 and your own on pre-5.1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With