Search code examples
iphoneiosmkmapviewmkannotationmkannotationview

Click on map pin gives wrong info


The problem I have is that when I click on the pin, it gives the wrong info associated with that same pin. I think the index of the pin may not be the same has the index of the array.

Here is the code:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    MKAnnotationView *pinView = nil;

    if(annotation != mapView.userLocation)
    {
        static NSString *defaultPinID = @"com.invasivecode.pin";
        pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
        if ( pinView == nil )
            pinView = [[MKAnnotationView alloc]
                       initWithAnnotation:annotation reuseIdentifier:defaultPinID];
        pinView.canShowCallout = YES;

        if ((annotation.coordinate.latitude == mapLatitude) && (annotation.coordinate.longitude == mapLongitude)) {

                if ([estadoUser isEqualToString:@"online"])
                    {
                       // NSLog(@"ONLINE");
                        pinView.image = [UIImage imageNamed:@"1352472516_speech_bubble_green.png"];    //as suggested by Squatch
                    }else{
                        //NSLog(@"OFFLINE");
                        pinView.image = [UIImage imageNamed:@"1352472468_speech_bubble_red.png"]; 
                    }
        }
        UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        [rightButton setTitle:annotation.title forState:UIControlStateNormal];
        [rightButton addTarget:self
                        action:@selector(showDetails)
              forControlEvents:UIControlEventTouchUpInside];
        pinView.rightCalloutAccessoryView = rightButton;

    } else {
        [mapView.userLocation setTitle:@"I am here"];
        }
    return pinView;
}

-(void)showDetails
{
    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];
    DMChatRoomViewController *_controller = [storyboard instantiateViewControllerWithIdentifier:@"DmChat"];
    [self presentViewController:_controller animated:YES completion:nil];
}

-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    if ([view.annotation isKindOfClass:[DisplayMap class]])
    {   
         NSInteger index = [mapView.annotations indexOfObject:view.annotation]      

        DisplayMap *annotation = (DisplayMap *)view.annotation;

        NSMutableDictionary *item = [allMapUsers objectAtIndex:index];

    //HERE DOES NOT DISPLAY THE INFO ON THE CORRECT PLACE

        NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
        [standardUserDefaults setObject:[[allMapUsers objectAtIndex:index] objectId] forKey:@"userSelecionadoParaChat"];


        [standardUserDefaults synchronize];
    }  
}

-(void)reloadMap
{
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    for (int i=0; i<allMapUsers.count; i++)
    {
        NSMutableDictionary *item = [allMapUsers objectAtIndex:i];

        NSLog(@"index=%i  para objectID=%@",i,[[allMapUsers objectAtIndex:i] objectId]);

        if (([[item valueForKey:@"estado"] isEqualToString:@"offline"] && [[defaults stringForKey:@"showOfflineUsers"] isEqualToString:@"no"]) || [[item valueForKey:@"estado"] isEqualToString:@""]) {

        }else{

        estadoUser = [item valueForKey:@"estado"];

        [outletMapView setMapType:MKMapTypeStandard];
        [outletMapView setZoomEnabled:YES];
        [outletMapView setScrollEnabled:YES];

        MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
        region.center.latitude = [[item valueForKey:@"Latitude"] floatValue];

        region.center.longitude = [[item valueForKey:@"Longitude"] floatValue];

        region.span.longitudeDelta = 81;
        region.span.latitudeDelta = 80;
        [outletMapView setRegion:region animated:YES];
        /////
        mapLatitude = [[item valueForKey:@"Latitude"] floatValue];
        mapLongitude = [[item valueForKey:@"Longitude"] floatValue];

        CLLocationCoordinate2D locationco = {mapLatitude,mapLongitude};

        ann = [[DisplayMap alloc] init];
        ann.coordinate = locationco;


        ann.title =   [item valueForKey:@"username1"];
            NSLog(@"ann.title=%@  para objectID=%@",[item valueForKey:@"username1"],[[allMapUsers objectAtIndex:i] objectId]);
        ann.subtitle = [item valueForKey:@"estado"];
        ann.coordinate = region.center;
        [outletMapView addAnnotation:ann];

        }
    }
}

Sorry for my bad english and if you dont understand the question please dont downrate, just ask, im always around to answer.

Best Regards


Solution

  • In didSelectAnnotationView, this code:

    NSInteger index = [mapView.annotations indexOfObject:view.annotation]      
    DisplayMap *annotation = (DisplayMap *)view.annotation;
    NSMutableDictionary *item = [allMapUsers objectAtIndex:index];
    

    won't always work because the map view's annotations array is in no way guaranteed to have the annotations in the same order that you added them. (See MKMapView annotations changing/losing order? and How to reorder MKMapView annotations array for details on this point.)

    You cannot assume at all that the index of the annotation in the mapView.annotations array is the same index of the annotation's source data in your allMapUsers array.


    What you can do instead is keep a reference to the source object in the annotation itself.

    For example, add an NSMutableDictionary property to your DisplayMap class:

    @property (nonatomic, retain) NSMutableDictionary *sourceDictionary;
    

    When creating the annotation, set the property:

    ann = [[DisplayMap alloc] init];
    ann.sourceDictionary = item;  // <-- keep ref to source item
    ann.coordinate = locationco;
    

    Then in didSelectAnnotationView:

    DisplayMap *annotation = (DisplayMap *)view.annotation;
    NSMutableDictionary *item = annotation.sourceDictionary;
    


    Another possible problem is here in viewForAnnotation:

    pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotation...
    if ( pinView == nil )
        pinView = [[MKAnnotationView alloc] ...
    pinView.canShowCallout = YES;
    

    If the dequeue returns a previously-used view, it's annotation property will still be pointing to the annotation it was previously used for. When using a dequeued view, you must update its annotation property to the current annotation:

    pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotation...
    if ( pinView == nil )
        pinView = [[MKAnnotationView alloc] ...
    else
        pinView.annotation = annotation;  // <-- add this
    pinView.canShowCallout = YES;
    

    See MKMapView Off Screen Annotation Image Incorrect for more details.