Domanda

I have a custom NSObject class Trails that I want to save to a plist. I have it correctly saving and loading now, with the exception of one part. I have a custom MKOverlay class object Crumbs in that class that I want to save as well. No error is thrown since crumbs conforms to NSCoding however the overlay does not display on the map after saving and reopening the app. The crumb object is not nil, but still does not display.

Trails.h

@interface Trails : NSObject <NSCoding>
{
    @public
    int topSpeed;
    float avgSpeed;
}
@property (nonatomic, strong) NSString *miles;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) NSString *time;
@property (nonatomic, strong) CrumbPath *crumbs;
- (NSString *)displayDate;
- (NSString *)displayStartTime;
- (void)addTopSpeed: (int)top withAvgSpeed:(float)avg;
@end

Trails.m

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:miles forKey:@"miles"];
    [coder encodeObject:date forKey:@"date"];
    [coder encodeObject:time forKey:@"time"];
    [coder encodeObject:crumbs forKey:@"crumbs"];
    [coder encodeFloat:avgSpeed forKey:@"avgSpeed"];
    [coder encodeInt:topSpeed forKey:@"topSpeed"];

}


- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if( self != nil )
    {
        miles = [coder decodeObjectForKey:@"miles"];
        date = [coder decodeObjectForKey:@"date"];
        time = [coder decodeObjectForKey:@"time"];
        crumbs = [coder decodeObjectForKey:@"crumbs"];
        avgSpeed = [coder decodeFloatForKey:@"avgSpeed"];
        topSpeed = [coder decodeIntForKey:@"topSpeed"];
    }
    return self;
}

Crumbs.h

@interface CrumbPath : NSObject <MKOverlay, NSCoding>

Crumbs.m

#import "CrumbPath.h"

#define INITIAL_POINT_SPACE 1000
#define MINIMUM_DELTA_METERS 10.0

@implementation CrumbPath

@synthesize points, pointCount;

- (id)initWithCenterCoordinate:(CLLocationCoordinate2D)coord
{
    self = [super init];
    if (self)
    {
        // initialize point storage and place this first coordinate in it
        pointSpace = INITIAL_POINT_SPACE;
        points = malloc(sizeof(MKMapPoint) * pointSpace);
        points[0] = MKMapPointForCoordinate(coord);
        pointCount = 1;

        // bite off up to 1/4 of the world to draw into.
        MKMapPoint origin = points[0];
        origin.x -= MKMapSizeWorld.width / 8.0;
        origin.y -= MKMapSizeWorld.height / 8.0;
        MKMapSize size = MKMapSizeWorld;
        size.width /= 4.0;
        size.height /= 4.0;
        boundingMapRect = (MKMapRect) { origin, size };
        MKMapRect worldRect = MKMapRectMake(0, 0, MKMapSizeWorld.width, MKMapSizeWorld.height);
        boundingMapRect = MKMapRectIntersection(boundingMapRect, worldRect);

        // initialize read-write lock for drawing and updates
        pthread_rwlock_init(&rwLock, NULL);
    }
    return self;
}

- (void)dealloc
{
    free(points);
    pthread_rwlock_destroy(&rwLock);
}

- (CLLocationCoordinate2D)coordinate
{
    return MKCoordinateForMapPoint(points[0]);
}

- (MKMapRect)boundingMapRect
{
    return boundingMapRect;
}

- (void)lockForReading
{
    pthread_rwlock_rdlock(&rwLock);
}

- (void)unlockForReading
{
    pthread_rwlock_unlock(&rwLock);
}

- (MKMapRect)addCoordinate:(CLLocationCoordinate2D)coord
{
    // Acquire the write lock because we are going to be changing the list of points
    pthread_rwlock_wrlock(&rwLock);

    // Convert a CLLocationCoordinate2D to an MKMapPoint
    MKMapPoint newPoint = MKMapPointForCoordinate(coord);
    MKMapPoint prevPoint = points[pointCount - 1];

    // Get the distance between this new point and the previous point.
    CLLocationDistance metersApart = MKMetersBetweenMapPoints(newPoint, prevPoint);
    MKMapRect updateRect = MKMapRectNull;

    if (metersApart > MINIMUM_DELTA_METERS)
    {
        // Grow the points array if necessary
        if (pointSpace == pointCount)
        {
            pointSpace *= 2;
            points = realloc(points, sizeof(MKMapPoint) * pointSpace);
        }    

        // Add the new point to the points array
        points[pointCount] = newPoint;
        pointCount++;

        // Compute MKMapRect bounding prevPoint and newPoint
        double minX = MIN(newPoint.x, prevPoint.x);
        double minY = MIN(newPoint.y, prevPoint.y);
        double maxX = MAX(newPoint.x, prevPoint.x);
        double maxY = MAX(newPoint.y, prevPoint.y);

        updateRect = MKMapRectMake(minX, minY, maxX - minX, maxY - minY);
    }

    pthread_rwlock_unlock(&rwLock);

    return updateRect;
}

//Conform to NSCoding
- (void)encodeWithCoder:(NSCoder *)coder {
    //I think I need to do something here?
}


- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if( self != nil )
    {
        //I think I need to do something here?
    }
    return self;
}

@end
È stato utile?

Soluzione

Your implementations of encodeWithCoder and initWithCoder for the CrumbPath class are empty. You need to save all properties/ivars, same as you did for the Trails object. All objects are responsible for saving their own properties/ivars with regards to NSCoding.

Just to add - you can't directly write CLLocationCoordinate2D and CGRect to disk. You'll have to break them up and reconstitute them. For example, with CGRect, you would have to encode as follows:

 [aCoder encodeFloat:rect.origin.x forKey:@"x"];
 [aCoder encodeFloat:rect.origin.y forKey:@"y"];
 [aCoder encodeFloat:rect.size.width forKey:@"width"];
 [aCoder encodeFloat:rect.size.height forKey:@"height"];

and then when decoding/init'ing, you have to reconstitute it:

 CGFloat x = [aDecoder decodeFloatForKey:@"x"];
 CGFloat y = [aDecoder decodeFloatForKey:@"y"];
 CGFloat width = [aDecoder decodeFloatForKey:@"width"];
 CGFloat height = [aDecoder decodeFloatForKey:@"height"];

 self.rect = CGRectMake(x,y,width,height);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top