Question

I am centering a map around the user's location or alternatively around a default location with a zoom level of roughly 30 miles. The problem is I can zoom in but cannot zoom out.

Whenever I try to zoom out on my iPhone 5C, the map immediately zooms back in when I remove my finger pinch.

I tried adding the mapView methods regionDidChangeAnimated and didChangeDragState to make certain I was resetting the region and center of the view, but neither of those seem to changed the ability to zoom out.

Cannot figure out what I missing here. Thanks for your help!

//
//  MapViewController.m


#import "MapViewController.h"
#import "MeetFetcher.h"
#import "MeetLocationAnnotation.h"
#import "MyAnnotations.h"


NSInteger const redPin = 0;
NSInteger const greenPin = 1;
NSInteger const purplePin = 2;

@interface MapViewController() <MKMapViewDelegate, CLLocationManagerDelegate>
@property (strong, nonatomic) IBOutlet MKMapView *mapView;

@end

@implementation MapViewController

@synthesize mapView = _mapView;
@synthesize annotations = _annotations;
@synthesize delegate = _delegate;
@synthesize beginningDateForSearch = _beginningDateForSearch;
@synthesize levelOfCompetition = _levelOfCompetition;
@synthesize meets = _meets;
@synthesize myLocationMngr = _myLocationMngr;


#pragma mark - Synchronize Model and View

- (NSArray *)mapAnnotations
{
    NSMutableArray *meetAnnotations = [NSMutableArray arrayWithCapacity:[self.meets count]];
    NSInteger iCount = 0;
    for (NSDictionary *meet in self.meets) {
        NSLog(@"In [%@ %@], iCount = %d and meet = %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), iCount, meet);
        [meetAnnotations addObject:[MeetLocationAnnotation annotationForMeet:meet]];
        iCount++;
        NSLog(@"\n \n meetAnnotations = %@",meetAnnotations);
    }
    return meetAnnotations;
}

- (void)updateMapView
{
    if (self.mapView.annotations) [self.mapView removeAnnotations:self.mapView.annotations];
    if (self.annotations) [self.mapView addAnnotations:self.annotations];
}

- (void)setMapView:(MKMapView *)mapView
{
    _mapView = mapView;
    [self updateMapView];
}

- (void)setAnnotations:(NSArray *)annotations
{
    _annotations = annotations;
    [self updateMapView];
}

- (void)setMeets:(NSArray *)meets
{
    if (_meets != meets) {
        _meets = meets;
    }
        // Model changed, so update our View in map
    NSLog(@"meets count in setMeets = %d", [meets count]);
    NSArray* listOfAnnotations = [self mapAnnotations];
    [self setAnnotations:listOfAnnotations];
    [self updateMapView];
}

#pragma mark - MKMapViewDelegate

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    MKPinAnnotationView *aView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"MapVC"];
    if (!aView) {
        aView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"MapVC"];
        aView.canShowCallout = YES;
        aView.rightCalloutAccessoryView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
        aView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        aView.pinColor = myPinColor;

        // could put a rightCalloutAccessoryView here
    } else {

    aView.annotation = annotation;
    }


//    [(UIImageView *)aView.rightCalloutAccessoryView setImage:nil]; // Could replace rightCallOutButton with image.

    return aView;
}

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)aView
{
    UIImage *image = [self.delegate mapViewController:self imageForAnnotation:aView.annotation];
    [(UIImageView *)aView.leftCalloutAccessoryView setImage:image];
}

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    NSLog(@"callout accessory tapped for annotation %@", [view.annotation title]);
}

#pragma mark - View Controller Lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.mapView.delegate = self;

    self.myLocationMngr = [[CLLocationManager alloc] init];
    self.myLocationMngr.delegate = self;
    self.myLocationMngr.desiredAccuracy = 500; // 500 meters.

    if( [CLLocationManager locationServicesEnabled] ) {
        myPinColor = MKPinAnnotationColorGreen;
        [self.myLocationMngr startUpdatingLocation];
    } else {
        CLLocationCoordinate2D defaultCoordinateWhenCLLocationDisabled;
        defaultCoordinateWhenCLLocationDisabled.latitude = 45.194014;
        defaultCoordinateWhenCLLocationDisabled.longitude = -117.862015;

        MyAnnotations *annotation =
        [[MyAnnotations alloc] initWithCoordinates:defaultCoordinateWhenCLLocationDisabled
                                            title:DEFAULT_LOCATION_TITLE
                                         subTitle:DEFAULT_LOCATION_SUBTITLE
                                  selectedPinColor:redPin];
//        [self.mapView addAnnotation:annotation];

        [self mapView:self.mapView didAddAnnotationViews:(NSArray *)annotation];
    }

    //Set some paramater for the location object.
    [self.myLocationMngr setDistanceFilter:kCLDistanceFilterNone];
    [self.myLocationMngr setDesiredAccuracy:kCLLocationAccuracyBest];

    //Set the first launch instance variable to allow the map to zoom on the user location when first launched.
    firstLaunch=YES;

}

- (void)viewDidUnload
{
    [super viewDidUnload];
    [self setMapView:nil];
}

-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *lastLocation =[locations lastObject];

    CLLocationAccuracy accuracy = [lastLocation horizontalAccuracy];
    NSLog(@"Received location %@ with accuracy %f", lastLocation, accuracy);

    if(accuracy < 100.0)
    {
        MKCoordinateSpan span = MKCoordinateSpanMake(0.14, 0.14);
        region = MKCoordinateRegionMake([lastLocation coordinate], span);

        [_mapView setRegion:region animated:YES];
        [manager stopUpdatingLocation];
    }
}


- (void)mapView:(MKMapView *)mv didAddAnnotationViews:(MKAnnotationView *)annotation
{

    //Zoom back to the user location after adding a new set of annotations.

    //Get the center point of the visible map.
    CLLocationCoordinate2D centre = [mv centerCoordinate];
    NSLog(@"centerCoordinate.Latitude = %f and centerCoordinate.Longitude = %f",centre.latitude, centre.longitude);

//    MKCoordinateRegion region;


    //If this is the first launch of the app then set the center point of the map to the user's location.
    if (firstLaunch) {
        region = MKCoordinateRegionMakeWithDistance(_myLocationMngr.location.coordinate,50000,50000);
        firstLaunch=NO;
    }else {
        //Set the center point to the visible region of the map and change the radius to match the search radius passed to the Google query string.
        region = MKCoordinateRegionMakeWithDistance(centre,currentDist,currentDist);

    }

    //Set the visible region of the map.
    [mv setRegion:region animated:YES];

}


-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    //Get the east and west points on the map so you can calculate the distance (zoom level) of the current map view.
    MKMapRect mRect = self.mapView.visibleMapRect;
    MKMapPoint eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect));
    MKMapPoint westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect));

    //Set your current distance instance variable.
    currentDist = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint);

    //Set your current center point on the map instance variable.
    currentCentre = self.mapView.centerCoordinate;
    region = MKCoordinateRegionMakeWithDistance(currentCentre,currentDist,currentDist);
}

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState
{
    MKMapRect mRect = self.mapView.visibleMapRect;
    MKMapPoint eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect));
    MKMapPoint westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect));

    //Set your current distance instance variable.
    currentDist = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint);

    //Set your current center point on the map instance variable.
    currentCentre = self.mapView.centerCoordinate;
    region = MKCoordinateRegionMakeWithDistance(currentCentre,currentDist,currentDist);
}

- (IBAction)refresh:(id)sender
{
    // might want to use introspection to be sure sender is UIBarButtonItem
    // (if not, it can skip the spinner)
    // that way this method can be a little more generic

    UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [spinner startAnimating];
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:spinner];

    dispatch_queue_t downloadQueue = dispatch_queue_create("meet downloader", NULL);
    dispatch_async(downloadQueue, ^{
        NSArray *meetsWithCoordinates = [MeetFetcher selectedGeoreferencedMeets:_beginningDateForSearch andCompetitionLevel:_levelOfCompetition];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.navigationItem.leftBarButtonItem = sender;
            NSLog(@"meetsWithCoordinates count = %d", [meetsWithCoordinates count]);
//            NSLog(@"%@",meetsWithCoordinates);
            myPinColor = MKPinAnnotationColorPurple;
            self.meets = meetsWithCoordinates;
        });
    });
}

@end
Was it helpful?

Solution

You shouldn't call didAddAnnotationViews, you should add the annotation and let iOS call the delegate method if it does indeed add the annotations.

Every time annotationViews are added you're changing the region. I suspect you meant to do that every time new annotations are added so add the setRegion call in the same place you addAnnotations. The views might be being added on a different sequence than you expect, for example panning or zooming out might reveal more annotations and thus the views for those annotations are drawn.

OTHER TIPS

So here is what I learned using Anna and Craig's suggestions. First off I was using the wrong method to determine whether app had permission to use location services. Then I put a switch in to determine whether the initial zoom level had already been set.

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.mapView.delegate = self;

    self.myLocationMngr = [[CLLocationManager alloc] init];
    self.myLocationMngr.delegate = self;
    self.myLocationMngr.desiredAccuracy = 500; // 500 meters.

    if([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
        myPinColor = MKPinAnnotationColorGreen;
        [self.myLocationMngr startUpdatingLocation];
        if (!haveSetZoomLevel) {
            CLLocation *currentLocation = [self.myLocationMngr location];
            CLLocationCoordinate2D currentCoordinates = [currentLocation coordinate];
            region = MKCoordinateRegionMakeWithDistance(currentCoordinates,50000,50000);
            [self.mapView setRegion:region animated:YES];
            haveSetZoomLevel = YES;
        }
    }

    //Set some paramater for the location object.
    [self.myLocationMngr setDistanceFilter:kCLDistanceFilterNone];  
    [self.myLocationMngr setDesiredAccuracy:kCLLocationAccuracyBest];

}

Then I used

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    if([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
    // zoom to user's location
    } else {
    // zoom to a default location
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top