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..
Related
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.
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.
In my mapview, the user can make his own map pin by tapping on the screen. He can set multiple pins. However, my code for saving only saves the last pin he has made and not the previous ones. How do I make it save the previous ones as well?
- (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 alloc] init];
CLLocationCoordinate2D coordinate = { touchMapCoordinate.latitude, touchMapCoordinate.longitude };
[pincoords addObject:[NSValue valueWithBytes:&coordinate objCType:#encode(CLLocationCoordinate2D)]];
MapAnnotation *toAdd = [[MapAnnotation alloc]init];
toAdd.coordinate = touchMapCoordinate;
toAdd.title = #"Svampe Spot";
[self.mapView addAnnotation:toAdd];
//Save pin section..
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:pincoords forKey:#"saveCoords"];
[userDefaults synchronize];
}
EDIT: So i made some changes. I made an NSMutable array and added the coordinates to it. However, the app terminates when trying to make a pin due to this: [NSUserDefaults setObject:forKey:]: attempt to insert non-property list object.
Everytime i try to add coordinates to mutable array i seem to get that message, what am i doing wrong?
Every time you write a new pin to the user defaults, it overwrites the old one. Instead, write an NSArray or NSMutableArray to the user defaults and add/remove pins from it.
UPDATE:
First of all, the NSMutableArray needs to be copied from the user defaults, not alloc/inited. This way, the array will already contain the previously saved points in it:
NSMutableArray *pincoords = (NSMutableArray *)[[NSUserDefaults standardUserDefaults] objectForKey:#"saveCoords"];
This will return a mutable array with your previously saved coordinates.
As for the method you are using to store the coordinate in the array, I think you have the right idea, but I've personally never done it that way. NSArrays can only store objects, and CLLocationCoordinate2D is not an object (probably a C struct like NSRect, I've never used it before). It needs to be converted to a NSCoding compliant object, such as NSString, NSNumber, or NSDictionary. What you could do is create a function to manually enter the coordinate values (wrapped in an NSNumber) into a NSDictionary (using keys such as #"Lat" and #"Long" for your coordinates), and then store the NSDictionary in your mutable array.
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.