Search code examples
iosuiimageviewnsdatanskeyedarchivernscoder

Unarchiving UIImageView with subviews


I'm going a bit crazy trying to archive and unarchive a UIImageView which has number of subviews which are custom views derived from UIImageView. Here's what I've done:

Add a category to the project to allow for the fact that UIImage does not conform to NSCoding:

#import "UIImage-NSCoding.h"
#define kEncodingKey @"UIImage"

@implementation UIImage(NSCoding)

- (id)initWithCoder:(NSCoder *)decoder
{
    if ((self = [super init]))
    {
        NSData *data = [decoder decodeObjectForKey:kEncodingKey];
        self = [self initWithData:data];
    }        
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
    NSData *data = UIImagePNGRepresentation(self);
    [encoder encodeObject:data forKey:kEncodingKey];
}

@end

The UIImageView I'm archiving is a property of my main view controller called "background". I save it like this:

NSData *dataToSave = [NSKeyedArchiver archivedDataWithRootObject:self.background];
[dataToSave dataWithContentsOfFile:imagePath options:NSDataWritingAtomic error:nil];

Then later I unarchive it like this:

NSData *savedData = [NSData dataWithContentsOfFile:imagePath];    
UIImageView *restoredImageView = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];
self.background.image = restoredImageView.image; // this works - archived image is displayed

Now I want to get my subviews (or one of them at least!) to display along with the unarchived root object:

NSLog(@"Subviews: %d", [restoredImageView.subviews count]); // this tells me my subviews have been archived along with the root object
NSLog(@"Subview 0: %@", [restoredImageView.subviews objectAtIndex:0]); // and they exist as expected

NSLog(@"Subviews: %d", [self.background.subviews count]); // at this point no subviews are present
[self.background addSubview:[restoredImageView.subviews objectAtIndex:0]]; // subview is not visible on screen
NSLog(@"Subviews: %d", [self.background.subviews count]); // count is one, but where is it?

ORUImageView *oru = [[ORUImageView alloc] init]; // I try creating the subview myself
oru = [self.background.subviews objectAtIndex:0]; 
[self.background addSubview:oru]; // it still doesn't show on screen
[self.background bringSubviewToFront:oru]; // makes no difference

NSLog(@"String: %@", oru.testString); // this correctly logs the test string I added to my custom class

This is what I've added to my ORUImageView subclass:

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [super encodeWithCoder:aCoder];    
    [aCoder encodeObject:self.testString forKey:@"theString"];
    [aCoder encodeObject:self.image forKey:@"theImage"]; // not sure if this is needed
}


- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    [self setTestString:[aDecoder decodeObjectForKey:@"theString"]];
    [self setImage:[aDecoder decodeObjectForKey:@"theImage"]]; // not sure if this is needed
    return self;
}

I've tried this with and without the encoding for the image property.

I'm suspect the problem is related to this property as I'm clearly getting the subview added to the unarchived view - it's just not visible! I feel the image property is not getting set, but I can't find a way to set it myself and I can't find anything that tells me the correct way to do this.


Solution

  • Hmm. I just tried this:

    - (IBAction)doButton1:(id)sender {
        theData = [NSKeyedArchiver archivedDataWithRootObject:iv];
        [iv removeFromSuperview];
        iv = nil;
    }
    
    - (IBAction)doButton2:(id)sender {
        iv = [NSKeyedUnarchiver unarchiveObjectWithData:theData];
        [self.view addSubview:iv];
    }
    

    It worked - with an ordinary UIImageView. So now I guess I don't understand what you're getting at; it appears that a UIImageView is already archived with its image. So you must be trying to solve some other problem. But I'm not grasping what the problem is.