Whereami maintain user zoom


After completing the whereami solution as described in ch 5 of the book, I started thinking about how to enhance it. One thing is that the solution always sets the zoom to a fixed distance, e.g. 500m. I was thinking that it would be good to maintain whatever zoom the user has selected. I started playing with mapView:didUpdateUserLocation. I couldn’t figure a way to determine the region distance, but I can access the region span. I use that to create a new region, centered around the updated location.

  • (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)u
    CLLocationCoordinate2D loc = [u coordinate];

    // use current span
    MKCoordinateSpan span = [mapView region].span;
    MKCoordinateRegion region = MKCoordinateRegionMake(loc, span);

    // as described in the book
    // MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 500, 500);

    [worldView setRegion:region animated:YES];

This doesn’t quite do what I want to do. First of all, the zoom SHOULD be based on a fixed distance the first time it finds a location. However, after that, as the user zooms in and out, the region should put the new location in the center, but keep the span fixed.

I guess I could use a static variable as a counter so that the method behaves differently the first time the method is called, but I’m thinking there is a more elegant solution. Has anyone done this or have insight into how this could be accomplished?


This is what I implemented, using a static BOOL

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)u { static BOOL firstTime = YES; CLLocationCoordinate2D loc = [u coordinate]; MKCoordinateRegion region; if (firstTime) { region = MKCoordinateRegionMakeWithDistance(loc, 500, 500); firstTime = NO; } else { // make a new region using the current span region = MKCoordinateRegionMake(loc, [mapView region].span); } [worldView setRegion:region animated:YES]; }

It works. Just looking for other approaches


Have you tried it without the if statement - that is, creating a region with the distance each time? If the distance is constant, the zoom probably won’t change.


I didn’t quite understand your suggestion, because it seems your implementation does exactly as you are suggesting (using a fixed distance) so I’ll try to restate my question. From that maybe you can gain insight into what I don’t understand and you can restate your suggestion so I get it. :slight_smile:

In your implementation (the book), when the user launches the program, it finds the current location and zooms in to some reasonable distance, say 250m or 500m. However, the extended behavior I’m trying to create is one that respects the current zoom when creating regions based on updated locations. Let’s say a user on the move (e.g. on a train) is interactively using gestures to zoom in or out as he/she is watching the traversed path on the map. Unfortunately, every time the location is updated, the user-selected zoom is replaced by the hard-coded constant distance. So if the user decides to zoom out to get a wider view of the town he/she is traveling through, the view gets abruptly re-zoomed to the constant distance whenever the location is updated (which could be quite frequently). I was thinking that the newly created region should use the existing span, but just update the location (I couldn’t find a way to access the current distance, so I was forced to deal with the zoom using span instead of distance).

Instead of using MKCoordinateRegionMakeWithDistance(loc, dist, dist), I use MKCoordinateRegionMake(loc, span), where span is the current span prior to the updated location.

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)u
    CLLocationCoordinate2D loc = [u coordinate];
    MKCoordinateRegion region;

    // make a new region using the current span
     region = MKCoordinateRegionMake(loc, [mapView region].span);

    [worldView setRegion:region animated:YES];

The problem with this is that the first time didUpdateUserLocation: is called, the map is zoomed out to the entire world (I think you even made a reference to the location marker being the size of Brazil).

Therefore, when the application first determines the user location, it should zoom using some reasonable distance. However, after that, the zoom should only be modified by the interactive user. That’s where I got the idea of using a static BOOL to differentiate how the region gets created based on whether it’s the first time or not. It doesn’t strike me as an overly elegant approach.

Is there a way that [mapview region].span could be initialized prior to the first call to didUpdateUserLocation? That way I wouldn’t ever need to deal with zooming inside calls to didUpdateUserLocation.