问题
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?
回答1:
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:
- The
pindict
variable is declared but never allocated. This will either lead to a crash when you callsetObject:forKey:
on it or do nothing. You need to allocate thepindict
variable. - With
NSUserDefaults
,objectForKey:
will return an immutable object. That means even thoughpincoords
is declared as anNSMutableArray
,objectForKey:
will return anNSArray
(if it finds a value for the key in user defaults). Therefore, theaddObject
calls will again fail (this time with a crash) since you can't add objects to anNSArray
. What you need to do is take the result ofobjectForKey:
and make a mutable version of it by callingmutableCopy
. - After adding the new coordinates to
pinCoords
, the code does not save the updated array back to user defaults. It just callssynchronize
. Before callingsynchronize
, you need to actually save the array to user defaults by callingsetObject: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):
- Instead of saving the coordinates as
float
values, save them asdouble
. The latitude and longitude in Core Location are actually of typedouble
(CLLocationDegrees
is actually a double). You'll also get better precision. So inaddPinToMap:
, usenumberWithDouble
instead ofnumberWithFloat
and inviewDidLoad
, usedoubleValue
instead offloatValue
. - In
viewDidLoad
, inside thefor
loop, you are callingsynchronize
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];
}
回答2:
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.
回答3:
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];
}
来源:https://stackoverflow.com/questions/21468053/save-and-load-mapannotation-pins-using-nsmutablearray