Question

In my app I have a table view that toggles the annotations on a map view. I can go back and forth between the table view and map view through the tab bar controller. My map will reload the annotations (from the selected items that are in the table view) on view did appear. I need to know when those annotations are done loading so that I can run my method that zooms to the generated region determined by the annotation cluster.

The problem was when I ran the zoom method directly after the addAnnotations method in the view did appear, it would start the zoom process before my annotations could get the correct coordinates. Thus resulting in an incorrect zoom, and my annotations moving to the correct location.

I would also like to note that I am using forward geocoding to get my annotations coordinates.

Here is my view did appear:

[super viewDidAppear:animated];

[businessMap removeAnnotations:businessPoints];
[businessPoints removeAllObjects];

UINavigationController *navVC = (UINavigationController *) [self.tabBarController.viewControllers objectAtIndex:0];

FirstViewController *VC = [navVC.viewControllers objectAtIndex:0];

for (businessInfo *business_info in VC.selectedBusinessesArray) {



    businessInfoAnnotation *businessAnnotation = [[businessInfoAnnotation alloc] init];

    businessAnnotation.businessInfoClass = business_info;

    CLGeocoder *geocoder = [[CLGeocoder alloc] init];

    [geocoder geocodeAddressString:business_info.location
                 completionHandler:^(NSArray* geocoded, NSError* error){
                     if (geocoded && geocoded.count > 0) {
                         CLPlacemark *placemark = [geocoded objectAtIndex:0];
                         CLLocation *location = placemark.location;
                         CLLocationCoordinate2D business_cords = location.coordinate;
                         businessAnnotation.coordinate = business_cords;
                     }
                 }];

    businessAnnotation.title = business_info.name;

    [businessPoints addObject:businessAnnotation];

}

[businessMap addAnnotations:businessPoints];

[businessMap setZoomEnabled:YES];

[self zoomToFitMapAnnotations:businessMap withArray:businessPoints];

Here is my zoom method:

-(void)zoomToFitMapAnnotations:(MKMapView*)mapViews withArray:(NSArray*)anAnnotationArray
{
    if([mapViews.annotations count] == 0) return;

    CLLocationCoordinate2D topLeftCoord;
    topLeftCoord.latitude = -90;
    topLeftCoord.longitude = 180;
    NSLog(@"zooming");

    CLLocationCoordinate2D bottomRightCoord;
    bottomRightCoord.latitude = 90;
    bottomRightCoord.longitude = -180;



    for(MKPointAnnotation* annotation in anAnnotationArray)
    {
        topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude);
        topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude);

        bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude);
        bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude);
    }

    MKCoordinateRegion region;
    region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5;
    region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5;
    region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1;
    region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1;

    region = [mapViews regionThatFits:region];
    [businessMap setRegion:region animated:YES];
}

Your help is appreciated, thanks. Sorry if this is sloppy, this is my first post.

Edit:
Here is my edited geocoder method according to nevan king's answer.

[geocoder geocodeAddressString:business_info.location
             completionHandler:^(NSArray* geocoded, NSError* error){
                 if (geocoded && geocoded.count > 0) {
                     CLPlacemark *placemark = [geocoded objectAtIndex:0];
                     CLLocation *location = placemark.location;
                     CLLocationCoordinate2D business_cords = location.coordinate;
                     businessAnnotation.coordinate = business_cords;

                     businessAnnotation.title = business_info.name;

                     [businessPoints addObject:businessAnnotation];

                     [businessMap addAnnotations:businessPoints];

                     [self zoomToFitMapAnnotations:businessMap withArray:businessPoints];
                 }
             }
];

Also not that I tried using the count of the annotation array to determine the last annotation's geocode completionHandler to change the region only on that specific one. But this produced an inconsistent result for my region. This is the only way it was consistently keeping all annotations within view.

Was it helpful?

Solution 3

It seems the best way to run a method after all annotations have been geocoded and loaded on the map is to run a simple count and an if statement checking the count number with an array count.

Here is what I came up with: int pointsArrayCount = contactArray.count; NSLog(@"Count : %d", pointsArrayCount); int numberOfPoints = pointsArrayCount; NSLog(@"Count Number : %d", numberOfPoints); i = 0;

for (contactInfo *contact_info in contactArray) {

    contatcInfoAnnotation *annotation = [[contatcInfoAnnotation alloc] init];

    annotation.contactInfoClass = contact_info;

    CLGeocoder *geocoder = [[CLGeocoder alloc] init];

    [geocoder geocodeAddressString:contact_info.address
                 completionHandler:^(NSArray* geocoded, NSError* error){
                     if (geocoded && geocoded.count > 0) {
                         CLPlacemark *placemark = [geocoded objectAtIndex:0];
                         CLLocation *location = placemark.location;
                         CLLocationCoordinate2D contact_cords = location.coordinate;
                         annotation.coordinate = contact_cords;

                         annotation.title = contact_info.name;

                         [mapPoints addObject:annotation];

                         [mapView addAnnotations:mapPoints];

                         i++;

                         NSLog(@"Count Number of Geocoded: %d", i);

                         if(i == numberOfPoints) {
                             [self zoomToFitMapAnnotations:mapView withArray:mapPoints];
                         }
                     }
                 }//end completionHandler
     ];

However, the completion handler is only able to handle up to 40 some geocodes. So this only works well when you are loading small amounts to your map while geocoding. If you are doing more than that, then you should be storing all the coordinates somewhere and then loading them separately when the map loads.

OTHER TIPS

MKMapViewDelegate has a mapView:didAddAnnotationViews: method which is called after a group of annotation views gets placed on the map. Note that it's not the same as annotations (some annotations might not be visible depending on the zoom).

Edit: I just noticed that you're geocoding the locations first. In that case, you'll have to add the annotations to the map in the geocode completion handler. Do it on the main thread. In your code, at the time you call addAnnotations: the completion block hasn't finished yet.

You could use a dispatch group, but I believe you'd have to manually use dispatch_group_enter and dispatch_group_leave, rather than dispatch_group_async. That way you can enter the group before each call to geocodeAddressString, and leave the group when its completion block finished. When all the completion blocks have completed, dispatch_notify would call your code for the resizing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top