I have an array with over 200 objects and I am trying to perform a loop through each of them.
Each object will have a yes/no field and I want to display a different coloured marker dependent on that yes / no value.
From what I can see is happening my loop is going through each object first and then all the annotation is added at the end for each object .
Since I perform a check within my loop through the array on the yes no value when all the annotation is added to my map, it will use the yes/no value from the last object in the array when it goes to plot for all.
How can I have it so that the marker will be different dependent on the yes/no value for each individual element?
My code is
for (i = 0; i < [appDelegate.itemArray count]; i++) {
item_details *tempObj = [appDelegate.itemArray objectAtIndex:i];
location.latitude = [tempObj.lat floatValue];
location.longitude = [tempObj.lon floatValue];
current_yesno = tempObj.yesno;
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc]initWithTitle:tempObj.name andCoordinate:location];
[self.mapView addAnnotation:newAnnotation];
[newAnnotation release];
}
with my annotation code as follows
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
if(current_yesno == YES){
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
annView.animatesDrop=NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
}
and current_yesno is declared in my .h file.
The viewForAnnotation delegate method isn't necessarily called immediately after you do addAnnotation and it can also be called at other times by the map view when it needs to get the view for an annotation (while your code is doing something completely different).
So you can't depend on the value of an ivar being in sync with some code outside that delegate method.
Instead, add the yesno property to your custom MapViewAnnotation class, set it when creating the annotation and then access its value in viewForAnnotation through the annotation parameter (ie. the map view is giving you a reference to the exact annotation object it wants the view for).
Example:
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc] init...
newAnnotation.yesno = tempObj.yesno; // <-- set property in annotation
[self.mapView addAnnotation:newAnnotation];
Then in viewForAnnotation:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
if (![annotation isKindOfClass:[MapViewAnnotation class]])
{
// Return nil (default view) if annotation is
// anything but your custom class.
return nil;
}
static NSString *reuseId = #"currentloc";
MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annView == nil)
{
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annView.animatesDrop = NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
}
else
{
annView.annotation = annotation;
}
MapViewAnnotation *mvAnn = (MapViewAnnotation *)annotation;
if (mvAnn.yesno)
{
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
return annView;
}
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier: #"id"];
if (pin == nil)
{
pin = [[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"id"] ;
}
else
{
pin.annotation = annotation;
}
pin.pinTintColor=[UIColor blueColor];
pin.canShowCallout = true;
Related
Hope someone can help, I'm trying to implement dropping one single Pin on a mapView. But I want the pin to be able to be dragged around and and dropped and for this annotation to be separate from the annotation for the user's location. Whilst the below code works, it creates duplicate pins for a user's touch and the pins can not be dragged around:
- (void)dropPinFromPress:(UIGestureRecognizer *)recognizer {
// Add Pin from User's Touch
if (recognizer.state != UIGestureRecognizerStateBegan) {
return;
}
// convert touched position to map coordinate
CGPoint userTouch = [recognizer locationInView:self.mapView];
CLLocationCoordinate2D mapPoint = [self.mapView convertPoint:userTouch toCoordinateFromView:self.mapView];
// Add Pin from user's touch
Annotation *pin = [[Annotation alloc]initWithLocation:mapPoint];
[self.mapView addAnnotation:pin];
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKAnnotationView *a = [ [ MKAnnotationView alloc ] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
// a.title = #"test";
if ( a == nil )
a = [ [ MKAnnotationView alloc ] initWithAnnotation:annotation reuseIdentifier: #"currentloc" ];
NSLog(#"%f",a.annotation.coordinate.latitude);
NSLog(#"%f",a.annotation.coordinate.longitude);
CLLocation* currentLocationMap = [[[CLLocation alloc] initWithLatitude:a.annotation.coordinate.latitude longitude:a.annotation.coordinate.longitude] autorelease];
[self coordinatesToDMS:currentLocationMap];
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:a reuseIdentifier:#"currentloc"];
if(a.annotation.coordinate.longitude == mapView.userLocation.coordinate.longitude || a.annotation.coordinate.latitude == mapView.userLocation.coordinate.latitude )
{
if ([annotation isKindOfClass:MKUserLocation.class])
{
//user location view is being requested,
//return nil so it uses the default which is a blue dot...
return nil;
}
//a.image = [UIImage imageNamed:#"userlocation.png"];
//a.pinColor=[UIColor redColor];
}
else
{
// annView.image =[UIImage imageNamed:#"map-pin.png"];
//PinFirst=FALSE;
//annView.pinColor = [UIColor redColor];
// annView.annotation=annotation;
}
annView.animatesDrop = YES;
annView.draggable = YES;
annView.canShowCallout = YES;
return annView;
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
NSLog(#"map Drag");
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState;
{
NSLog(#"pin Drag");
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = view.annotation.coordinate;
NSLog(#"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
CLLocation* draglocation = [[[CLLocation alloc] initWithLatitude:droppedAt.latitude longitude:droppedAt.longitude] autorelease];
}
}
I have an MKMapView, that I drop a number of pins on from an array. Each map pin has an MKAnnotationView that links to a segue and another controller.
All of this is working perfectly now, but I can't see how to pass a variable through the MKAnnotationView to the segue.
I can work with either passing a string or an integer, as I could pass a username as a string, or pass the id of the array and use that in the second view controller after the segue.
My working code for looping through my array, dropping the pins, adding the annotations, and then calling the segue is as below.
This all works fine, populates the map, drops the bins, and the annotations work and link to the new segue. This is all perfect!
I just need to be able to pass a variable through from the map pin array to the second view controller segue.
- (void)loadMapPins {
for (int i=0; i<maparray.count; i++){
Location *item = _feedItems1[i];
CLLocationCoordinate2D myCoordinate = {[item.latitude doubleValue], [item.longitude doubleValue]};
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = myCoordinate;
point.title = item.firstname;
point.subtitle = [NSString stringWithFormat: #"%#", item.username];
[self.mapView addAnnotation:point];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {
if (annotation == self.mapView.userLocation) return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* customPin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
customPin.pinColor = MKPinAnnotationColorRed;
customPin.animatesDrop = YES;
customPin.canShowCallout = YES;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPin.rightCalloutAccessoryView = rightButton;
return customPin;
}
- (IBAction)showDetails:(id)sender {
[self performSegueWithIdentifier:#"showProfileFromMap" sender:self];
}
You should use this MKMapViewDelegate method
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
CustomAnnotationClass *annotation = view.annotation;
selectedAnnotationVariable = annotation.yourProperty;
[self performSegueWithIdentifier:#"performSegue" sender:self];
}
I wrote this code to create a custom annotation image
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *google = #"googlePin";
if ([annotation isKindOfClass:[myClass class]])
{
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:google];
if (!annotationView)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:google];
annotationView.image = [UIImage imageNamed:#"pin.png"];
}
return annotationView;
}
return nil;
}
The image is appearing on the map; however when I click on it nothing happen, no title nor subtitle.
Do you guys have any idea?
When you override viewForAnnotation, you have to set canShowCallout to YES (the default on a new view you alloc/init is NO).
If you don't override that delegate method, the map view creates a default red pin with canShowCallout already set to YES.
However, even with canShowCallout set to YES, the callout will still not appear if the annotation's title is nil or blank (empty string).
(But again, if the title is not nil and not blank, the callout won't show unless canShowCallout is YES.)
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:google];
if (!annotationView)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:google];
annotationView.image = [UIImage imageNamed:#"pin.png"];
annotationView.canShowCallout = YES; // <-- add this
}
else
{
// unrelated but should handle view re-use...
annotationView.annotation = annotation;
}
return annotationView;
I have an application that uses MKMapView and at some point in the app I need to remove all the current pins in the map using
[mapView removeAnnotations:mapView.annotations]
And then I want to show again the current user location
mapView.showUserLocation = YES
But I can only make it reappear as a regular pin, because the userLocation view class its not public so I cant return views of that type. Here is my code
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id<MKAnnotation>)annotation
MKPinAnnotationView* annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"currentloc"];
if (!annView) {
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
annView.pinColor = MKPinAnnotationColorRed;
annView.animatesDrop=TRUE;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
if ([annotation isKindOfClass:[DraggableAnnotationAM class]] ) annView.draggable =YES;
return annView;
}
else {
if ([annotation isKindOfClass:[DraggableAnnotationAM class]] ) annView.draggable = YES;
annView.annotation = annotation;
return annView;
}
Also I have found through reflection that
MKUserLocationView
is the class that is used to display the current location, but because its not public its not safe to use and my app keeps crashing and Im sure theres a easier way.
Is it possible to do what I want, or should I just never remove the user location annotation of the mapView?
Thanks in advance
If you only want to show a blue dot for the user's location in stead of a regular pin, return nil in this mapView delegate function. Alternatively return your own MKAnnotationView (subclass) object:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil // or return a custom MKAnnotationView
} else { // etc.
Try doing it with an NSMutableArray. Hope this helps you solving your problem!
NSMutableArray *annotation = [[NSMutableArray alloc] init];
for (id <MKAnnotation> annot_ in [mapView annotations])
{
if ( [annot_ isKindOfClass:[MKUserLocation class]] ) {
}
else {
[annotation addObject:annot_];
}
}
[mapView removeAnnotations:annotation];
[annotation release];
annotation = nil;
I am trying to remove all the annotations of a map. The class that contains the MKMapView implements the MKMapViewDelegate. I am calling the remove all annotations method when a button (displayed in a modal view) is clicked. I am using the next code to remove all the annotations.
- (void)removeAllAnnotations {
NSMutableArray *annotationsToRemove = [NSMutableArray arrayWithCapacity:[self.mapView.annotations count]];
for (int i = 0; i < [self.mapView.annotations count]; i++) {
if ([[self.mapView.annotations objectAtIndex:i] isKindOfClass:[AddressAnnotation class]]) {
[annotationsToRemove addObject:[self.mapView.annotations objectAtIndex:i]];
}
}
[self.mapView removeAnnotations:annotationsToRemove];
}
The code works right but after calling the method, and when I try to add new annotations to the empty map, the class does not call to the viewForAnnotations method and the annotations do not drop down and do not show a disclosure button into the annotation view. Why is this happening?
Thanks for reading.
Edited:
The annotations are shown but with out calling the view for annotation method (with out dropping down and with out including a disclosure button into the annotation view). Here is the method that I use to add the annotations and the viewForAnnotation method.
- (void)loadAnnotations:(NSString *)type {
NSString *path = [[NSBundle mainBundle] pathForResource:#"PlacesInformation" ofType:#"plist"];
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
for (int i = 0; i < tmpArray.count; i++) {
// Breaks the string down even further to latitude and longitude fields.
NSString *coordinateString = [[tmpArray objectAtIndex:i] objectForKey:#"coordinates"];
NSString *option = [[tmpArray objectAtIndex:i] objectForKey:#"type"];
if ([option isEqualToString:type]) {
CLLocationCoordinate2D currentCoordinate = [self stringToCoordinate:coordinateString];
AddressAnnotation *annotation = [[[AddressAnnotation alloc] initWithCoordinate:currentCoordinate] autorelease];
[self.mapView addAnnotation:annotation];
}
}
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
else { // Handles the other annotations.
// Try to dequeue an existing pin view first.
static NSString *AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if (!pinView) {
// If an existing pin view was not available, creates one.
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// Adds a detail disclosure button.
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
} else
pinView.annotation = annotation;
}
return nil;
}
In viewForAnnotation, in the case where you are reusing a view, you are currently returning nil.
This else part:
} else
pinView.annotation = annotation;
Should probably be:
} else {
pinView.annotation = annotation;
return pinView;
}