Tuesday, 27 October 2009

Changing the Annotation on the Current Location

The MKMapKit class comes with a property you can set to show the current location of the iPhone:

mapView.showCurrentLocation = YES;

This will show the little blue bubble on the map view and have the radar effect like Google Maps. However when the user touches the bubble it'll just show the text "Current Location". To set it to something other then this you can use:

mapView.userLocation.title = @"a different annotation title";

To set it to the address of that location you need to use the MKReverseGeocoder class, which is also found in the MapKit framework. You'll need to provide this delegate method in your viewController class:


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation


This gets called whenever the location changes. Within this method you can then call the Reverse Geocoder service using:

/* warning! use this sparingly */

MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc initWithCoordinate:newLocation.coordinate];

geocoder.delegate = self;

[geocoder start];


Next declare two more methods.

-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error

{

[geocoder release];

}


-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark

{

[geocoder release];

mapView.userLocation.title = placemark.title;

}


The last line of code will update the title of the bubble with the correct location.

Now for the important bit. The reverseGeocoder is making a call to a Google Web service and using the latitude and longitude you provide it will return a street address or place name. If you call this function everytime the user location changes then you'll be generating a huge amount of unnecessary network traffic from and to the iPhone and putting strain on the google servers to handle your constant address requests. To minimise this I would suggest you use one of two approaches. Either keep a instance variable to keep track of the distance travelled since the last time you requested a reverseGeocoder location and only start the reverseGeocoder when a certain limit has been reached (i.e. 50 meters). You can use the CLLocation method getDistanceFrom to find this distance. Or, you could declare a NSTimer then calls starts the reverseGeocoder only after a certain interval has passed, 10 seconds for example.

Best of luck implementing this.

1 comment:

dagjjadlgh said...

Careful about releasing the geocoder in the didFail and didFind methods. If the viewController unloads after you have started the geocoder, but before the server has responded, you will never release it and get a leak.