Unarchiving UIImageView with subviews
Asked Answered
E

1

2

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.

Emlin answered 17/2, 2012 at 19:49 Comment(0)
P
4

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.

Petree answered 17/2, 2012 at 19:59 Comment(5)
I'm getting the root object back and managing to display its image. But I'm unable to get its subviews to display. They're being archived and they're being unarchived, but where are they? I can't get them to display as subviews of the unarchived root view.Emlin
Maybe because of the unnecessary stuff you're doing with the UIImage. It works fine for me if iv in the above code has subviews.Petree
Ok thanks, that makes sense. It works if I add restoredImageView as a subview of self.view, as you have. But if I use { self.background = restoredImageView } nothing happens. (self.background is an IBOutlet UIImageView). Any idea why I can't swap restoredImageView into self.background?Emlin
Ok, thanks. I'm going to have to do a lot more experimentation with this it seems...Emlin
Well, the code I showed you is a complete example: it takes a UIImageView out of the interface and archives it, and later it unarchives it and puts it into the interface. And it brings it with the UIImageView's subviews and its image as part of the archiving/unarchiving. So I would suggest you start with that.Petree

© 2022 - 2024 — McMap. All rights reserved.