Search code examples
iphonecocoa-touchrefreshmkmapviewmkoverlay

Problem Refreshing iPhone MapView


Hey guys, I am having trouble getting overlays in my map view to refresh via the setNeedsDisplayInMapRect: function. Here is the relevant code:

ParkingMapViewController.m:

for (ParkingRegionOverlay *overlay in mapView.overlays) {
    [overlay setNeedsDisplayInMapRect:self.mapView.visibleMapRect];
}

//...
- (MKOverlayView *)mapView:(MKMapView *)mapView 
            viewForOverlay:(id <MKOverlay>)overlay
{   
    NSLog(@"ParkingMapViewController.m mapView:viewForOverlay");
    //...
}
//...

ParkingRegionOverlay.h:

@interface ParkingRegionOverlay : MKOverlayView <MKOverlay> {
    MKPolygon *polygon;
    MKMapRect boundingRect;
    CLLocationCoordinate2D centerCoord;
    //...
}
//...

And I am not getting the "ParkingMapViewController.m mapView:viewForOverlay" output to console I am expecting. I have walked through he debugger and have ensured that the for loop is being reached and executed, however mapView:viewForOverlay: isn't being called for some reason. Anyone know what I am doing wrong? Thanks in advance!

EDIT 1:

I believe I have set the delegate, coordinates, and bounding rect properly, but please take a look...

ParkingMapViewController.h

@interface ParkingMapViewController : UIViewController <MKMapViewDelegate> {
    MKMapView *mapView;
//...

ParkingMapViewController.m:

//...
- (void)viewDidLoad {
    [super viewDidLoad];
    mapView.delegate = self;
//...

ParkingRegionOverlay.m:

//...
//initializes polygon and calculates bounding rect as well as its center coordinate
-(id)initWithPoints:(NSArray *)pointsArray andTitle:(NSString *)overlayTitle{
    MKMapPoint points[[pointsArray count]];
    double maxX = MIN_COORD_VAL;
    double minX = MAX_COORD_VAL;
    double maxY = MIN_COORD_VAL;
    double minY = MAX_COORD_VAL;
    double tempX = 0;
    double tempY = 0;

    if (self = [super init]) {
        int i = 0;
        //determine min/max extrema to help calculate the bounding rect
        for (id coordDict in pointsArray){
            tempX = [[coordDict objectForKey:@"latitude"] doubleValue];
            tempY = [[coordDict objectForKey:@"longitude"] doubleValue];
            maxX = fmax(tempX, maxX);
            minX = fmin(tempX, minX);
            maxY = fmax(tempY, maxY);
            minY = fmin(tempY, minY);

            CLLocationCoordinate2D coord = {tempX,tempY};
            points[i] = MKMapPointForCoordinate(coord);
            i++;
        }//for

        CLLocationCoordinate2D northWestCorner = CLLocationCoordinate2DMake(maxX, minY);
        CLLocationCoordinate2D southEastCorner = CLLocationCoordinate2DMake(minX, maxY);
        MKMapPoint northWestPoint = MKMapPointForCoordinate(northWestCorner);
        MKMapPoint southEastPoint = MKMapPointForCoordinate(southEastCorner);
        boundingRect = MKMapRectMake(northWestPoint.x, northWestPoint.y, 
                                     (southEastPoint.x-northWestPoint.x), 
                                     (southEastPoint.y-northWestPoint.y));

        centerCoord = CLLocationCoordinate2DMake((maxX-minX)/2,(maxY-minY)/2);
        polygon = [MKPolygon polygonWithPoints:points count:[pointsArray count]];
        polygon.title = overlayTitle;

        [self initAcceptedPermitsBasedOnTitle:overlayTitle];
    }//if

    return self;
}
//...

Thanks.

EDIT 2:

An alternate method I have tried, to no avail:

ParkingMapViewController.m

    NSArray *overlayArray = [[NSArray alloc] initWithArray:[mapView overlays]];
    [self.mapView removeOverlays:mapView.overlays];
    [self.mapView addOverlays:overlayArray];

Removing and re-adding all overlays ain't working too well for me. It merely crashes when that third line is executed. Any ideas?

EDIT 3:

So I changed the previously posted code to the following:

NSArray *overlayArray = [mapView overlays];
[self.mapView removeOverlays:overlayArray];
[self.mapView addOverlays:overlayArray];

And am now seeing this in the console:

2011-05-05 14:24:54.145 Parking[68501:207] -[NSCFNumber boundingMapRect]: unrecognized selector sent to instance 0xa9afae0
2011-05-05 14:24:54.147 Parking[68501:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFNumber boundingMapRect]: unrecognized selector sent to instance 0xa9afae0'

Solution

  • So I figured it out. Not necessarily the most efficient method, but it works for me. This is what I did:

    [self.mapView removeOverlays:[mapView overlays]];
    [self loadOverlaysAndAnnotations];
    

    And here is loadOverlaysAndAnnotations:

    - (void)loadOverlaysAndAnnotations {
    
        NSError *error;
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        CoreDataSingleton *coreDataSingleton = [CoreDataSingleton sharedManager];
        NSEntityDescription *entity = [NSEntityDescription 
                                       entityForName:@"ParkingLot" inManagedObjectContext:[coreDataSingleton managedObjectContext]];
        [fetchRequest setEntity:entity];
        NSArray *fetchedObjects = [[coreDataSingleton managedObjectContext] executeFetchRequest:fetchRequest error:&error];
        for (NSManagedObject *overlayEntity in fetchedObjects) {
            NSArray *pointsArray = [NSArray arrayWithArray:[overlayEntity valueForKey:@"overlayCoordPoints"]];
            ParkingRegionOverlay *regionPolygon = [[ParkingRegionOverlay alloc] initWithPoints:pointsArray andTitle:[overlayEntity valueForKey:@"lotName"]];
            [mapView addOverlay:regionPolygon];
            [regionPolygon release];
    
    
            NSSet *annotationsSet = [NSSet setWithSet:[overlayEntity valueForKey:@"parkingAnnotations"]];
            NSArray *allAnnotations = [NSArray arrayWithArray:[annotationsSet allObjects]];
            CLLocationCoordinate2D workingCoordinate;
            for (ParkingAnnotations *annotation in allAnnotations) {
                ParkingAnnotation *parkingAnnot = [[ParkingAnnotation alloc] init];
                workingCoordinate.latitude = [[annotation latitude] doubleValue];
                workingCoordinate.longitude = [[annotation longitude] doubleValue];
                [parkingAnnot setCoordinate:workingCoordinate];
                [parkingAnnot setTitle:[overlayEntity valueForKey:@"lotName"]];
                if ([[overlayEntity valueForKey:@"lotName"] isEqualToString:@"VP 1"]) {
                    [parkingAnnot setLot:lot1];
                }
    
                [mapView addAnnotation:parkingAnnot];
                [parkingAnnot release];
            }
        }        
        [fetchRequest release];
    }//loadOverlaysAndAnnotations
    

    In short, I didn't have to create a new function but merely call the function I used to load overlays into the map view and that works fine! Hope this helps anyone else stuck in a similar situation.

    EDIT:

    Important to note that I am reloading BOTH annotations and overlays, and, if done without first removing both annotations and overlays, can lead to crashing of your app if the reload function is called too many times. This is what I am currently experiencing. Just something to be aware of. To fix this I am going to have separate load functions, one for overlays, and one for annotations which will be called appropriately.