Check if user has selected another annotation in map

1.8k views Asked by At

I have a map with annotations where I am performing the following actions:

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{
[self methodA];}


 - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
[self methodB];}

Both method A and B remove or add views in mapView.superview.

All works fine when dealing with one annotation. The Problem is when I have more than one annotation and I select two, one after the other.

If I click anywhere else in the map everything works fine. But when I click in the second annotationview after the first one, it performs "didDeselectAnnotationView" and then "didSelectAnnotationView" which calls both methods A and B, and it is not what I want. I would like it to detect that I am clicking in another annotation and ignore both methods.

I have researched about this but haven't found any solution yet.

I have tried to add an global variable and play with it as well as identifying where the user touches:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

UITouch *touch = [touches anyObject];

// Get the specific point that was touched
CGPoint point = [touch locationInView:self.mapView];

for (id<MKAnnotation> annotation in self.mapView.annotations) {
    MKAnnotationView* anView = [self.mapView viewForAnnotation: annotation];
    if (anView){

        CGRect frame = CGRectMake(anView.frame.origin.x-5, anView.frame.origin.y-5, anView.frame.size.width+5, anView.frame.size.height+5);
        if ( CGRectContainsPoint(frame, point) ) {

            NSLog(@"BELONGS");
        }

    }

}}

However, this didn't catch all the touches plus it would be a bit of a spaghetti solution.

Any ideas how to solve this?

All the best

4

There are 4 answers

3
ffarquet On

The delegate doesn't do any distinction between the case where you unselect and then select another annotation view and the case where you select a new annotation view while one is already selected. In both cases, these calls will be done in that order:

  • didDeselect view1
  • didSelect view2

You want 2 things :

1) not call methodB when selection of view2 is very close (in time) to a deselection of view1.

2) not call methodA when a selection is close (in time) to a previous selection.

To resolve those two problems, you can store two timestamps: 1) last selection 2) last deselection. With these two variables you can set a threshold to define what "close" mean in the two rules above.

Now you have a condition for preventing the call of methodA and methodB in the specific case of a fast deselection and selection (which is what really happens for the delegate when you switch from one selection to another one).

0
jonypz On

Here is my current solution. Even though I don't love it, it works for now. If you have a better one please post an answer and I will mark it as the right one.

I am using a global variable to keep track of the current selected annotation. I initialise it as nil.

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{

[self performSelector:@selector(numberOfSelectedAnnotations)
           withObject:view.annotation afterDelay:.5];
}

 - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
      if (self.selectedAnnotation == nil) {
         [self methodB];
         }
     self.selectedAnnotation = (MKAnnotationView*)view;
}


-(void)numberOfSelectedAnnotations{
if (self.mapView.selectedAnnotations.count == 0) {
    [self methodA];
    self.selectedAnnotation = nil;
    }
}
0
Nikita Khandelwal On
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)aView
{
    indexPathTag=aView.tag;
    [mapView deselectAnnotation:aView.annotation animated:YES];

}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)aView
{
}

call deselectAnnotation from didSelectAnnotationView.

0
kylejs On

This is my preferred solution:

private var selectedAnnotationView: MKAnnotationView?

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    if let _ = annotation as? MKUserLocation {
        return nil
    }
    let ident = "MyAnnotation"
    let pin = mapView.dequeueReusableAnnotationViewWithIdentifier(ident) as? MKPinAnnotationView ?? MKPinAnnotationView(annotation: annotation, reuseIdentifier: ident)
    pin.animatesDrop = true
    pin.canShowCallout = true
    pin.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "annotationTapped:"))
    pin.rightCalloutAccessoryView = UIButton(type: .InfoDark)
    return pin
}

// This will get called before didDeselectAnnotationView below
func annotationTapped(tapGesture: UITapGestureRecognizer) {
    if let annotationView = tapGesture.view as? MKAnnotationView {

        if annotationView === self.selectedAnnotationView {
            // same as last time
        } else {
            // different annotation view
        }

        self.selectedAnnotationView = annotationView

        // any other logic for handling annotation view tap
    }
}

func mapView(mapView: MKMapView, didDeselectAnnotationView view: MKAnnotationView) {
    if view === self.selectedAnnotationView {

        // The user did not select another annotation view
        // (e.g. they tapped the map)            

        self.selectedAnnotationView = nil
    }
}