Question

I need help with my MapAnnotation codes. I want to save map pins so that the user can come back to the map and see the ones he/she has placed.

However this code does not show the pins when the view loads.

Code for adding and saving pins:

- (void)addPinToMap:(UIGestureRecognizer *)gestureRecognizer
{

if (gestureRecognizer.state != UIGestureRecognizerStateBegan)
    return;



CGPoint touchPoint = [gestureRecognizer locationInView:self.mapView];
CLLocationCoordinate2D touchMapCoordinate =
[self.mapView convertPoint:touchPoint toCoordinateFromView:self.mapView];

 //NSMutableArray *pincoords = (NSMutableArray *)[[NSUserDefaults standardUserDefaults] objectForKey:@"saveCoords"];

NSMutableDictionary *pindict;

NSUserDefaults *def = [NSUserDefaults standardUserDefaults];

NSMutableArray *pincoords = [def objectForKey:@"saveCoords"];


CLLocationCoordinate2D coordinates;

coordinates.latitude = touchMapCoordinate.latitude;
coordinates.longitude = touchMapCoordinate.longitude;

NSNumber* latDegrees = [NSNumber numberWithFloat:touchMapCoordinate.latitude];
NSNumber* longDegrees = [NSNumber numberWithFloat:touchMapCoordinate.longitude];



 [pindict setObject:latDegrees forKey:@"pinlat"];
  [pindict setObject:longDegrees forKey:@"pinlong"];








 MapAnnotation *toAdd = [[MapAnnotation alloc]init];


toAdd.coordinate = coordinates;
toAdd.title = @"Svampe Spot";

[self.mapView addAnnotation:toAdd];


[pincoords addObject:pindict];

[def synchronize];


}

My code for loading:

- (void)viewDidLoad
{

 [super viewDidLoad];


   NSUserDefaults *def = [NSUserDefaults standardUserDefaults];

   NSMutableArray *pincoords = [def objectForKey:@"saveCoords"];

   for (NSDictionary *pindict in pincoords) {

     CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([[pindict objectForKey:@"pinlat"] floatValue], [[pindict objectForKey:@"pinlong"] floatValue]);

    MapAnnotation *mapPin = [[MapAnnotation alloc]init];





    mapPin.coordinate = coordinate;
    mapPin.title = @"Svampe Spot";

    [self.mapView addAnnotation:mapPin];
    [def synchronize];


    [def synchronize];

 }

}

A solution would be much appreciated!!

CODE FOR DELETING A SPECIFIC ANNOTATION:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)mapViewpin
calloutAccessoryControlTapped:(UIControl *)control {


NSUserDefaults *def = [NSUserDefaults standardUserDefaults];

NSMutableArray *pincoords = [def objectForKey:@"saveCoords"];

for (NSDictionary *pindict in pincoords) {

 CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(
                                                               [[pindict objectForKey:@"pinlat"] doubleValue],
                                                               [[pindict objectForKey:@"pinlong"] doubleValue]);

 MapAnnotation *mapPin = mapViewpin.annotation;

 mapPin.coordinate =coordinate;

 [self.mapView removeAnnotation:mapPin];
 [def removeObjectForKey:@"saveCoords"];

 [def synchronize];
}


}

This deletes them all. How do i make it specific for the annotation whos button is tapped?

Was it helpful?

Solution

Volker's answer does point out one important issue:

The first time the user adds a pin, there will be no array already saved in the user defaults so pinCoords will be nil. Therefore, the addObject calls on the array will do nothing. You need to handle this case by allocating an empty array if it's nil so you can add objects to it.


However, there are still other issues in addPinToMap: that will prevent the code from working even after fixing the above:

  1. The pindict variable is declared but never allocated. This will either lead to a crash when you call setObject:forKey: on it or do nothing. You need to allocate the pindict variable.
  2. With NSUserDefaults, objectForKey: will return an immutable object. That means even though pincoords is declared as an NSMutableArray, objectForKey: will return an NSArray (if it finds a value for the key in user defaults). Therefore, the addObject calls will again fail (this time with a crash) since you can't add objects to an NSArray. What you need to do is take the result of objectForKey: and make a mutable version of it by calling mutableCopy.
  3. After adding the new coordinates to pinCoords, the code does not save the updated array back to user defaults. It just calls synchronize. Before calling synchronize, you need to actually save the array to user defaults by calling setObject:forKey:.

After making all the above changes, the code should work.


I also recommend two more changes to improve the code (though these aren't preventing it from working):

  1. Instead of saving the coordinates as float values, save them as double. The latitude and longitude in Core Location are actually of type double (CLLocationDegrees is actually a double). You'll also get better precision. So in addPinToMap:, use numberWithDouble instead of numberWithFloat and in viewDidLoad, use doubleValue instead of floatValue.
  2. In viewDidLoad, inside the for loop, you are calling synchronize twice. These calls are pointless -- remove them.


The final, updated code in addPinToMap: would be this:

- (void)addPinToMap:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateBegan)
        return;

    CGPoint touchPoint = [gestureRecognizer locationInView:self.mapView];
    CLLocationCoordinate2D touchMapCoordinate = [self.mapView convertPoint:touchPoint toCoordinateFromView:self.mapView];

    NSMutableDictionary *pindict = [NSMutableDictionary dictionary];

    NSUserDefaults *def = [NSUserDefaults standardUserDefaults];

    NSMutableArray *pincoords = [[def objectForKey:@"saveCoords"] mutableCopy];
    if (pincoords == nil)
    {
        pincoords = [NSMutableArray array];
    }

    CLLocationCoordinate2D coordinates;

    coordinates.latitude = touchMapCoordinate.latitude;
    coordinates.longitude = touchMapCoordinate.longitude;

    NSNumber* latDegrees = [NSNumber numberWithDouble:touchMapCoordinate.latitude];
    NSNumber* longDegrees = [NSNumber numberWithDouble:touchMapCoordinate.longitude];

    [pindict setObject:latDegrees forKey:@"pinlat"];
    [pindict setObject:longDegrees forKey:@"pinlong"];

    MapAnnotation *toAdd = [[MapAnnotation alloc]init];

    toAdd.coordinate = coordinates;
    toAdd.title = @"Svampe Spot";

    [self.mapView addAnnotation:toAdd];

    [pincoords addObject:pindict];

    [def setObject:pincoords forKey:@"saveCoords"];

    [def synchronize];
}

The updated for loop in viewDidLoad would be like this:

for (NSDictionary *pindict in pincoords) {

    CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(
        [[pindict objectForKey:@"pinlat"] doubleValue], 
        [[pindict objectForKey:@"pinlong"] doubleValue]);

    MapAnnotation *mapPin = [[MapAnnotation alloc]init];

    mapPin.coordinate = coordinate;
    mapPin.title = @"Svampe Spot";

    [self.map addAnnotation:mapPin];
}

OTHER TIPS

Initially user des contain no mutable array for the key. So when accessing the object for the key do check if non nil is returned and otherwise create a new mutable array for filling in the points.

To address your issue with removing just one of the markers, Anna is right and you have to add a line like such to your creation method for an identifier to look for:

[pindict setObject:NAME_OF_YOUR_MARKER_TITLE forKey:@"name"];

And then do the following to see if it exists, remove it from the array of locations and then save the data:

 myMapAnnotation *selectedAnnotation = mapViewpin.annotation;

    //Iterate through the location markers
    for (int i = 0; i<pincoords.count; i++) {

        if ([pincoords[i] containsObject: selectedAnnotation.title]) {

            //Create a new mutable array to work with
            NSMutableArray *pincoords = [[def objectForKey:@"saveCoords"]mutableCopy];

            //Remove the marker
            [pincoords removeObjectAtIndex:i];
            [self.mapView removeAnnotation:selectedAnnotation];

            //Update the data
            [def setObject:pincoords forKey:@"saveCoords"];
        }

        //Sync outside the loop
        [def synchronize];

    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top