9

I'm currently adding annotations to my map through a loop... but the annotations are only appearing on my map in groups. Also, on load, only about 4 annotations are actually displayed on the map... but as I move the map a little, all of the annotations that should be there, suddenly appear.

How can I get all of the annotations to load in the right place, one at a time?

Thanks in advance!

Here is the code I'm using to add annotations:

 NSString *incident;
            for (incident in weekFeed) {
                NSString *finalCoordinates = [[NSString alloc] initWithFormat:@"%@", [incident valueForKey:@"coordinates"]];

                NSArray *coordinatesArray = [finalCoordinates componentsSeparatedByString:@","]; 

                latcoord = (@"%@", [coordinatesArray objectAtIndex:0]);
                longcoord = (@"%@", [coordinatesArray objectAtIndex:1]);

                // Final Logs
                NSLog(@"Coordinates in NSString: [%@] - [%@]", latcoord, longcoord);

                CLLocationCoordinate2D coord;
                coord.latitude = [latcoord doubleValue];
                coord.longitude = [longcoord doubleValue];


                DisplayMap *ann = [[DisplayMap alloc] init]; 
                ann.title = [NSString stringWithFormat: @"%@", [incident valueForKey:@"incident_type"]];
                ann.subtitle = [NSString stringWithFormat: @"%@", [incident valueForKey:@"note"]];
                ann.coordinate = coord;

                [mapView addAnnotation:ann];

                [ann release];
                }


// Custom Map Markers
-(MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation {

    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;  //return nil to use default blue dot view

    static NSString *AnnotationViewID = @"annotationViewID";
    MKAnnotationView *annotationView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];

    if (annotationView == nil) {
        annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID] autorelease];
        }

    annotationView.canShowCallout = YES;

    if ([annotationView.annotation.title isEqualToString:@"one"]) {
        UIImage *pinImage = [UIImage imageNamed:@"marker_1.png"];
        [annotationView setImage:pinImage];
        }

    if ([annotationView.annotation.title isEqualToString:@"two"]) {
        UIImage *pinImage = [UIImage imageNamed:@"marker_2.png"];
        [annotationView setImage:pinImage];
        }

    annotationView.annotation = annotation;
    return annotationView;
    }

- (void) mapView:(MKMapView *)mapV didAddAnnotationViews:(NSArray *)views {
    CGRect visibleRect = [mapV annotationVisibleRect]; 
    for (MKAnnotationView *view in views) {
        CGRect endFrame = view.frame;

        CGRect startFrame = endFrame; startFrame.origin.y = visibleRect.origin.y - startFrame.size.height;
        view.frame = startFrame;

        [UIView beginAnimations:@"drop" context:NULL]; 
        [UIView setAnimationDuration:0.4];

        view.frame = endFrame;

        [UIView commitAnimations];
    }
}
Adam Storr
  • 1,438
  • 2
  • 21
  • 46
  • Could you post your code to add annotations? – Nick B Jul 06 '11 at 02:36
  • Thanks! Could you post the viewForAnnotation: method too? – Nick B Jul 06 '11 at 02:50
  • Any advice Nick? Thanks for helping out. – Adam Storr Jul 06 '11 at 16:50
  • What method are you calling your loop from to populate your annotations? – Chip Coons Jul 08 '11 at 18:17
  • It's in a method called `loadmap` which runs in a thread: `[NSThread detachNewThreadSelector: @selector(loadMap) toTarget:self withObject:nil];` inside `viewWillAppear` – Adam Storr Jul 09 '11 at 04:52
  • Just to know, have you try with addAnnotations out of the loop instead call addAnnotation for each annotation? – Mat Jul 15 '11 at 18:51
  • Hi Please See this i hpoe you will get a proper way to implement multiple annotation view,. Click [here](https://developer.apple.com/library/ios/#samplecode/WeatherMap/Introduction/Intro.html)! – sandy Jul 10 '11 at 09:34

2 Answers2

1

Adam,

This solution is a bit messy as I had to munge up one of my current projects to test, but hopefully this will work for you.

First an explanation, it's critical to separate data from UI presentation. The [MKMapView addAnnotation(s)] are just a data update to MKMapView and have no direct impact on animation or timing.

The delegate method mapView:didAddAnnotationViews: is where all of the custom presentation behavior should be defined. In your description you didn't want these to appear all at once, so you need to sequence your animations instead of performing them simultaneously.

One method is to add all of the annotations at once and then just add them with increasing animation delays, however new annotations that get added for whatever reason will begin their animations at zero again.

The method below sets up an animation queue self.pendingViewsForAnimation (NSMutableArray) to hold annotation views as they are added and then chains the animation sequentially.

I've replaced the frame animation with alpha to focus on the animation problem to separate it from the issue of some items not appearing. More on this after the code...

// Interface
// ...

// Add property or iVar for pendingViewsForAnimation; you must init/dealloc the array
@property (retain) NSMutableArray* pendingViewsForAnimation;

// Implementation
// ...
- (void)processPendingViewsForAnimation
{
    static BOOL runningAnimations = NO;
    // Nothing to animate, exit
    if ([self.pendingViewsForAnimation count]==0) return;
    // Already animating, exit
    if (runningAnimations) 
        return;

    // We're animating
    runningAnimations = YES;

    MKAnnotationView* view = [self.pendingViewsForAnimation lastObject];

    [UIView animateWithDuration:0.4 animations:^(void) {
        view.alpha = 1;
        NSLog(@"Show Annotation[%d] %@",[self.pendingViewsForAnimation count],view);
    } completion:^(BOOL finished) {
        [self.pendingViewsForAnimation removeObject:view];
        runningAnimations = NO;
        [self processPendingViewsForAnimation];
    }];

}

// This just demonstrates the animation logic, I've removed the "frame" animation for now
// to focus our attention on just the animation.    
- (void) mapView:(MKMapView *)mapV didAddAnnotationViews:(NSArray *)views {
    for (MKAnnotationView *view in views) {
        view.alpha = 0;

        [self.pendingViewsForAnimation addObject:view];
    }
    [self processPendingViewsForAnimation];
}

Regarding your second issue, items are not always appearing until you move the map. I don't see any obvious errors in your code, but here are some things I would do to isolate the problem:

  1. Temporarily remove your mapView:didAddAnnotationViews:, mapView:annotationForView: and any other custom behaviors to see if default behavior works.
  2. Verify that you have a valid Annotation at the addAnnotation: call and that the coordinates are visible (use [mapView visibleMapRect], MKMapRectContainsPoint(), and MKMapPointForCoordinate().
  3. If it is still not functioning, look at where you are calling the add annotations code from. I try to avoid making annotation calls during map movement by using performSelector:withObject:afterDelay. You can precede this with an [NSObject cancelPreviousPerformRequestsWithTarget:selector:object:] to create a slight delay prior to loading annotations in case the map is being moved a long distance with multiple swipes.

One last point, to achieve the pin-drop effect you're looking for, you probably want to offset by a fixed distance from the original object instead of depending on annotationVisibleRect. Your current implementation will result in pins moving at different speeds depending on their distance from the edge. Items at the top will slowly move into place while items at the bottom will fly rapidly into place. Apple's default animation always drops from the same height. An example is here: How can I create a custom "pin-drop" animation using MKAnnotationView?

Hope this helps.

Update: To demonstrate this code in action I've attached a link to a modified version of Apple's Seismic demo with the following changes:

  1. Changed Earthquake.h/m to be an MKAnnotation object
  2. Added SeismicMapViewController.h/m with above code
  3. Updated RootViewController.h/m to open the map view as a modal page

See: http://dl.dropbox.com/u/36171337/SeismicXMLWithMapDelay.zip

Community
  • 1
  • 1
Barney Mattox
  • 2,964
  • 1
  • 16
  • 11
  • Wow! Thanks Barney. I added your code... no errors... but now there aren't any annotations appearing on the map. I'm going to play with the code a bit, but let me know if you it makes sense why that would happen. – Adam Storr Jul 14 '11 at 18:39
  • Did some tests... I found out that `-(void)processPendingViewsForAnimations` isn't making it past `if ([self.pendingViewsForAnimation count]==0) return;` – Adam Storr Jul 14 '11 at 19:12
  • Adam, the code I sent worked in the application I was testing, but I'll have to break it out into a sample to make sure you have everything you need. May be a couple of days before I can get the above updated. Stay tuned. (And make sure you allocated the array in your constructor (could be initWithNibNamed or initWithCoder or...other, so make sure it's getting called). – Barney Mattox Jul 16 '11 at 07:52
  • Thanks Barney... I look forward to it. – Adam Storr Jul 18 '11 at 18:35
  • Hi Barney... any luck? Thanks for your help. – Adam Storr Jul 23 '11 at 00:58
  • Adam, I've updated the above with a demonstration application based on Apple's Seismic example. There were no changes to the presented code, so hopefully this example will solve any implementation details. – Barney Mattox Jul 24 '11 at 07:08
  • Barney, the code you sent works great... but it isn't working in my application. I'm currently trying to figure out what else is wrong with my map... I think it might be because of multiple thread... – Adam Storr Jul 30 '11 at 17:29
  • Map view doesn't support multiple threads so you at least want to make sure all of your annotation logic is in the main thread or force it there with dispatch_async(dispatch_get_main_queue(),...) [going from memory so might have main function name wrong] – Barney Mattox Jul 31 '11 at 01:55
0

You will need to pause updating after you add each annotation and allow the map to have time to refresh.

Phil
  • 121
  • 4