I have a map app that allows the User to save annotations as a favorite to a mutable array. All favorite annotations can then be displayed when the User chooses to.
Annotations added to the mutable array are of the MKPointAnnotation class. I can correctly add annotations to the mutable array, but I haven't come up with a working solution that correctly removes a specific annotation from the mutable array. How can a specific annotation be removed from the mutable array that contains multiple annotations that were saved as favorites? Several non-working solutions are found in my sample code.
//** Correctly adds a favorite annotation to the mutable array favoriteAnnotationsArray **
-(void)addToFavoriteAnnotationsArray{
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
NSArray *components = [favoritesString componentsSeparatedByString:#","];
annotation.coordinate = CLLocationCoordinate2DMake([components[1] doubleValue], [components[0] doubleValue]);
annotation.title = [components[2] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
[self.favoriteAnnotationsArray addObject:annotation];
}
//** Need to remove a favorite annotation from the mutable array favoriteAnnotationsArray **
-(void)removeObjectFromFavoriteAnnotationsArray{
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
NSArray *components = [favoritesString componentsSeparatedByString:#","];
annotation.coordinate = CLLocationCoordinate2DMake([components[1] doubleValue], [components[0] doubleValue]);
annotation.title = [components[2] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
//** Used in first non-working solution below **
//NSMutableArray *objectToRemoveArray = [[NSMutableArray alloc] init];
//[objectToRemoveArray addObject:annotation];
//** The following three lines didn't remove any annotations from array **
//[self.favoriteAnnotationsArray removeObjectsInArray:objectToRemoveArray];
//[self.favoriteAnnotationsArray removeObject:annotation];
//[self.favoriteAnnotationsArray removeObjectIdenticalTo:annotation];
//** This only removes the last object in array and not necessarily the correct annotation to remove **
[self.favoriteAnnotationsArray removeLastObject];
}
You will need to specify an unique annotation from the favoriteAnnotationsArray in order for it o be removed successfully.
Maybe you can try something as follows:
-(void)removeAnnotationFromFavoriteAnnotationsArrayWithTitle: (NSString *) titleString {
for(int i=0; i<self.favoriteAnnotationsArray.count; i++) {
MKPointAnnotation *annotation = (MKPointAnnotation *)[self.favoriteAnnotationsArray objectAtIndex:i];
NSString * annotationTitle = annotation.title;
if([annotationTitle isEqualToString:titleString]) {
[self.favoriteAnnotationsArray removeObject:annotation];
break;
}
}
}
If title is not unique enough for you to differentiate between annotations, then you might consider subclassing MKAnnotation and add an unique property and pass it to the above function instead of the title.
One approach is to iterate through your annotation array and find the one to remove. For example:
- (void)addToFavoriteAnnotationsArray:(NSString *)string {
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
NSArray *components = [string componentsSeparatedByString:#","];
annotation.coordinate = CLLocationCoordinate2DMake([components[1] doubleValue], [components[0] doubleValue]);
annotation.title = [components[2] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
[self.favoriteAnnotationsArray addObject:annotation];
}
- (void)removeObjectFromFavoriteAnnotationsArray:(NSString *)string {
NSArray *components = [string componentsSeparatedByString:#","];
NSString *titleOfAnnotationToRemove = [components[2] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
CLLocationCoordinate2D coordinateOfAnnotationToRemove = CLLocationCoordinate2DMake([components[1] doubleValue], [components[0] doubleValue]);
for (NSInteger i = 0; i < self.favoriteAnnotationsArray.count; i++) {
id<MKAnnotation>annotation = self.favoriteAnnotationsArray[i];
if ([annotation.title isEqualToString:titleOfAnnotationToRemove] && coordinateOfAnnotationToRemove.latitude == annotation.coordinate.latitude && coordinateOfAnnotationToRemove.longitude == annotation.coordinate.longitude) {
[self.favoriteAnnotationsArray removeObjectAtIndex:i];
break;
}
}
}
Or if you wanted to match on title, alone, then remove the coordinates from the if statement above. Also note that I've added the string as a parameter to these methods. It's always better to pass a parameter to a method rather than relying upon some property.
Frankly, though, when the user is selecting one to remove, you probably don't want them to have to manually enter the name and coordinates again. You probably want them to pick one from the list. So you might show them a table view of the annotations and when they say they want to remove the third one, you'd do just pass the index to a method like this:
- (void)removeObjectFromFavoriteAnnotationsArray:(NSInteger)index {
[self.favoriteAnnotationsArray removeObjectAtIndex:index];
}
By the way, all of the above routines remove the annotation from your array. If you've also added this annotation to the map view, remember to remove it from there, too.
Related
Hai I am trying to display multiple annotations in mkmap. While am adding the coordinates it shows the error:"Assigning to 'CLLocationCoordinate2D' from incompatible type 'id'". I know this is a simple prob, but I have searched many times and tried a lot but none works, my code is,
for(int i=0 ; i<coordinates.count ; i++)
{
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate =[coordinates objectAtIndex:i]; //here it shows the error
point.title = #"title";
[self.mapView addAnnotation:point];
}
kindly advice me to solve this prob. Thank you...
As CLLocationCoordinate2D is not an Objective-C object, but a struct, it cannot be directly stored in an NSArray object, but rather it must be wrapped within an NSValue object.
Therefore the code to read co-ordinates from the array is probably:
CLLocationCoordinate2D coords;
[[coordinates objectAtIndex:i] getValue:&coords];
point.coordinate = coords;
However to know for sure, I would need to see how the array was created.
i have a Problem with the Annotiations, the Code is ok, but i read with the Variable [i] more than one Username and Location. On the Map i have only the last reading Username and Location Pin.
In the Debug Box , he show me all Names and Location, but i have only 1 Pin on the Map.
Here is the Code.
for (int i=1; i<4; ++i) {
[User whereKey:#"index" equalTo:#(i)];
PFObject *test1 = [User getFirstObject];
ann.title = [test1 objectForKey:#"username"] ;
PFGeoPoint *geopoint = [test1 objectForKey:#"currentLocation"];
CLLocationCoordinate2D coord = { geopoint.latitude, geopoint.longitude };
ann.coordinate = coord;
[MapView addAnnotation:ann];
}
Thanks for your Help ;)
The ann object which you are adding to the map needs to be created separately for each annotation that you add.
In the code shown, the ann object is not being created inside the for loop for each annotation so you end up with only one annotation object with the coordinates of the last item.
Create the ann object (alloc+init) inside the loop before setting the properties on it.
I have been trying to do this for a while now , but still not able to show multiple annotations.
Here is how i am storing data into NSMutableDictionary and then to NSMutableArray in AppDelegate
// set the values to mutable dictionary
[_locationDictionary setObject:latitudeObj forKey:#"latitudeValue"];
[_locationDictionary setObject:longitudeObj forKey:#"longitudeValue"];
[_locationDictionary setObject:fromUser forKey:#"fromUser"];
// add to array
[_presenceArray addObject:_locationDictionary];
And this i am doing inside another ViewController.
-(void)getPresenceData {
for(NSDictionary *dictObj in appDelegate.presenceArray) // this array that has dictionaries
{
annotation = [[Annotation alloc]init]; // Annotation Class
annotation.coordinate = CLLocationCoordinate2DMake([[dictObj valueForKey:#"latitudeValue"]doubleValue], [[dictObj valueForKey:#"longitudeValue"]doubleValue]);
annotation.title = [dictObj objectForKey:#"fromUser"];
[self.buddyDataArray addObject:annotation];
NSLog(#"buddy data array is %#", self.buddyDataArray);
[self.mapView addAnnotations:buddyDataArray];
}
You could try this way... Make sure in your presenceArray dictionaries are getting stored.
-(void)getPresenceData {
for(NSDictionary *dictObj in appDelegate.presenceArray) // this array that has dictionaries
{
annotation = [[Annotation alloc]init]; // Annotation Class
annotation.coordinate = CLLocationCoordinate2DMake([[dictObj valueForKey:#"latitudeValue"]doubleValue], [[dictObj valueForKey:#"longitudeValue"]doubleValue]);
annotation.title = [dictObj objectForKey:#"fromUser"];
[self.mapview addAnnotation:annotation];
}
}
As you are already looping than you dont need to create array of annotation objects.. You can add directly them to map as i did in above code..
This is my method
- (void)populateLocationsToSort {
//1. Get UserLocation based on mapview
self.userLocation = [[CLLocation alloc] initWithLatitude:self._mapView.userLocation.coordinate.latitude longitude:self._mapView.userLocation.coordinate.longitude];
//Set self.annotationsToSort so any new values get written onto a clean array
self.myLocationsToSort = nil;
// Loop thru dictionary-->Create allocations --> But dont plot
for (Holiday * holidayObject in self.farSiman) {
// 3. Unload objects values into locals
NSString * latitude = holidayObject.latitude;
NSString * longitude = holidayObject.longitude;
NSString * storeDescription = holidayObject.name;
NSString * address = holidayObject.address;
// 4. Create MyLocation object based on locals gotten from Custom Object
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
MyLocation *annotation = [[MyLocation alloc] initWithName:storeDescription address:address coordinate:coordinate distance:0];
// 5. Calculate distance between locations & uL
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
annotation.distance = calculatedDistance/1000;
//Add annotation to local NSMArray
[self.myLocationsToSort addObject:annotation];
**NSLog(#"self.myLocationsToSort in someEarlyMethod is %#",self.myLocationsToSort);**
}
//2. Set appDelegate userLocation
AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
myDelegate.userLocation = self.userLocation;
//3. Set appDelegate mylocations
myDelegate.annotationsToSort = self.myLocationsToSort;
}
In the bold line, self.myLocationsToSort is already null. I thought setting a value to nil was cleaning it out basically, ready to be re-used? I need to do so because this method is called once on launch and a second time after an NSNotification is received when data is gotten from the web. If I call this method again from the NSNotification selector, the new web data gets written on top of the old data and it spits out an inconsistent mess of values :)
Setting it to nil removes the reference to that object. If you are using ARC and it is the last strong reference to that object, the system autoreleases the object and frees its memory. In either case, it does not "clean it out and be ready for re-use", you need to re-allocate and initialize your object. If you would rather just remove all of the objects, and assuming myLocationsToSort is an NSMutableArray you can just call
[self.myLocationsToSort removeAllObjects];
Otherwise you need to do
self.myLocationsToSort = nil;
self.myLocationsToSort = [[NSMutableArray alloc] init];
In my app I fetch a coredatabase and put results into an array called self.stores. I convert those store locations to MyLocation objects which have a distance property. I plot the MyLocation objects like so:
- (void)plotStorePositions:(NSString *)responseString {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
//CYCLE THRU STORE OBJECTS
for (Store * storeObject in self.stores) {
NSString * latitude = storeObject.latitude;
NSString * longitude = storeObject.longitude;
NSString * storeDescription = storeObject.name;
NSString * address = storeObject.address;
//CREATE MYLOCATION OBJECTS
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
MyLocation *annotation = [[MyLocation alloc] initWithName:storeDescription address:address coordinate:coordinate distance:0];
//CREATE A PIN FOR EACH MYLOCATION ANNOTATION
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
//SET USERLOCATION (Must move this code out)
self.userLocation = [[CLLocation alloc] initWithLatitude:self._mapView.userLocation.coordinate.latitude longitude:self._mapView.userLocation.coordinate.longitude];
//SET USERLOCATION TO APPDELEGATE (Must move this code out)
AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
myDelegate.userLocation = self.userLocation;
//CALCULATE DISTANCE AND SET ITS THAT MYLOCATION's DISTANCE PROPERTY
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:userLocation];
annotation.distance = calculatedDistance/1000;
[_mapView addAnnotation:annotation];
}
}
This is in a mapview. I then have a tableview where I fetch the same coredatabase and get self.stores again. Then I cycle through that array of self.stores to create STORE storeObjects to put into my tableview cells. But I want to sort those cells. How do I get MyLocation objects which should be in memory? Do I need to pass them to the appDelegate or to the tableview controller?
As you are using Core Data, your best option is to use a NSFetchedResultsController to populate your table view.
All you need then is the managed object context, either from another controller or the app delegate.