Reputation: 89
I have a map on which I can add multiple annotations with a customisable subtitle. I want to put the annotations into an array with the updated subtitle.
Currently when I add an annotation it goes into the array, but I don't know how to update the array with the subtitle and remove the annotation from the array if it gets removed from the map.
I was also thinking to add all the annotations to the array when switching to the next view, but not sure how to do that.
My current code:
Adding the annotation with a UIGestureRecognizer
MapAnnotation *mapPoint = [[MapAnnotation alloc]init];
mapPoint.coordinate = touchMapCoordinate;
mapPoint.title = address;
mapPoint.subtitle = @"";
[self.map addAnnotation:mapPoint];
self.mapLatitudeString = [NSString stringWithFormat:@"%f",mapPoint.coordinate.latitude];
self.mapLongitudeString = [NSString stringWithFormat:@"%f",mapPoint.coordinate.longitude];
self.mapTitleString = [NSString stringWithFormat:@"%@", mapPoint.title];
self.mapSubtitleString = [NSString stringWithFormat:@"%@", mapPoint.subtitle];
self.mapAnnotation = [NSString stringWithFormat:@"%d,%@,%@,%@,%@",self.mapAnnotationArray.count, self.mapLatitudeString, self.mapLongitudeString, self.mapTitleString, self.mapSubtitleString];
if (!self.mapAnnotationArray) {
self.mapAnnotationArray = [[NSMutableArray alloc] init];
}
[self.mapAnnotationArray addObject:self.mapAnnotation];
NSLog(@"%@", self.mapAnnotationArray);
Editing the subtitle and removing the annotation from an alert view
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
}
if (buttonIndex == 1) {
if (self.map.selectedAnnotations.count > 0)
{
id<MKAnnotation> ann = [self.map.selectedAnnotations objectAtIndex:0];
if ([ann isKindOfClass:[MapAnnotation class]])
{
MKPointAnnotation *pa = (MKPointAnnotation *)ann;
pa.subtitle = [[alertView textFieldAtIndex:0] text];
}
}
}
if (buttonIndex == 2) {
[self.map removeAnnotations:self.map.selectedAnnotations];
}
}
Upvotes: 0
Views: 476
Reputation:
With the current approach of adding a formatted string representation (that includes the sequence number) of each annotation to the mapAnnotationArray
, the process of maintaining the array is tedious:
componentsSeparatedByString
),replaceObjectAtIndex:withObject:
removeObjectAtIndex:
These steps would have to be done every time you want to retrieve or search for a field in mapAnnotationArray
. It's not efficient or flexible (what if user decides to use a comma in the subtitle, what if address contains a comma, etc).
Instead, store the annotation objects themselves in mapAnnotationArray
and only generate the formatted string representation when you need to submit the data to the server. Storing a proper object in the array lets you take advantage object-oriented methods.
In the method that adds the annotation to the map, instead of adding the formatted string to mapAnnotationArray
, add the annotation itself:
[self.mapAnnotationArray addObject:mapPoint];
Where you update the annotation's subtitle
in the alert view delegate method, you don't need to do anything to mapAnnotationArray
now because the annotation object in the array is the exact same object you updated the subtitle
of. You can NSLog the array here to see the change.
Where you remove the annotation in the alert view delegate method, you need to remove the same object from mapAnnotationArray
(the map view doesn't know about your array):
if (buttonIndex == 2)
{
if (self.map.selectedAnnotations.count > 0)
{
id<MKAnnotation> ann = [self.map.selectedAnnotations objectAtIndex:0];
if ([ann isKindOfClass:[MapAnnotation class]])
{
[self.mapAnnotationArray removeObject:ann];
}
[self.map removeAnnotations:self.map.selectedAnnotations];
}
}
Next, to make it easier to generate the formatted string and to debug the contents of mapAnnotationArray
, add a helper method and an override for the description
method to your MapAnnotation
class:
-(NSString *)stringForServer
//You can name this method whatever you want.
//Builds the comma-delimited representation
//of this annotation (excluding the sequence number).
//Note this format still "breaks" if title or subtitle contain commas.
{
NSString *result = [NSString stringWithFormat:@"%f,%f,%@,%@",
self.coordinate.latitude,
self.coordinate.longitude,
self.title,
self.subtitle];
return result;
}
-(NSString *)description
//This method must be named this.
//When you NSLog this object, it will call this method to get
//what string to display for it.
{
return [NSString stringWithFormat:@"%@ (%@)",
[super description], [self stringForServer]];
}
Finally, wherever you submit the data to the server, loop through the array and generate the formatted strings from the annotation objects (and prefix the sequence number). This example just loops through the array and NSLogs the formatted string for each annotation:
for (int i=0; i < self.mapAnnotationArray.count; i++)
{
MapAnnotation *ma = (MapAnnotation *)[self.mapAnnotationArray objectAtIndex:i];
NSString *maString = [ma stringForServer];
NSString *fullStringForServer = [NSString stringWithFormat:@"%d,%@", i, maString];
NSLog(@"maa[%d] = %@", i, fullStringForServer);
}
Prefixing the sequence number when you are actually ready to save the data avoids re-numbering and duplication problems. Of course, this assumes that annotations don't have to keep the same sequence number across saves.
Also consider using JSON instead of a comma-delimited format. NSJSONSerialization
makes it relatively easy. See Convert an iOS objective c object to a JSON string for an example.
Upvotes: 1