Implementing the Location Manager Delegate
Based
on the XIB we just laid out, the application starts up with a message
and a spinner that let the user know we are waiting on the initial
location reading from Core Location. We’ll request this reading as soon
as the view loads in the view controller’s viewDidLoad method.
When the location manager delegate gets a reading, we’ll calculate the
distance to Cupertino, update the label, hide the activity indicator
view, and unhide the distance view.
Click the CupertinoViewController.m file in the Classes group. Synthesize the four properties we added to the CupertinoViewController header file and release them in the dealloc method.
Uncomment the viewDidLoad method, and instantiate a location manager with the view controller itself as the delegate and a desiredAccuracy of kCLLocationAccuracyThreeKilometers and a distanceFilter of 1,609 meters (1 mile). Start the updates with the startUpdatingLocation method. The implementation should resemble Listing 2.
Listing 2.
- (void)viewDidLoad {
[super viewDidLoad];
locMan = [[CLLocationManager alloc] init];
locMan.delegate = self;
locMan.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locMan.distanceFilter = 1609; // a mile
[locMan startUpdatingLocation];
}
|
Now we need to implement the two methods of the location manager delegate protocol. We’ll start with the error condition: locationManager:didFailWithError. In the case of an error getting the current location, we already have a default message in place in the distanceLabel, so we’ll just remove the waitView with the activity monitor and show the distanceView. If the user denied access to Core Location updates, we will also clean up the location manager request. Implement locationManager:didFailWithError as shown in Listing 3.
Listing 3.
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error {
if (error.code == kCLErrorDenied) {
// Turn off the location manager updates
[manager stopUpdatingLocation];
[locMan release];
locMan = nil;
}
waitView.hidden = YES;
distanceView.hidden = NO;
}
|
Our final method (locationManager:didUpdateLocation:fromLocation)
will do the dirty work of calculating the distance to Cupertino. To do
this, we obviously need a location in Cupertino that we can compare to
the user’s current location. According to http://gpsvisualizer.com/geocode, the center of Cupertino, California, is at 37.3229978 latitude, –122.0321823 longitude. Add two constants for these values (kCupertinoLatitude and kCupertinoLongitude) after the #import line in the CupertinoViewController implementation file:
#define kCupertinoLatitude 37.3229978
#define kCupertinoLongitude -122.0321823
This brings us to one more hidden gem in CLLocation. We don’t need to write our own longitude/latitude distance calculations because we can compare two CLLocation instances with the distanceFromLocation method. In our implementation of locationManager:didUpdateLocation:fromLocation, we will create a CLLocation
instance for Cupertino and compare it to the instance we get from Core
Location to get the distance in meters. We’ll then convert the distance
to miles, and if it’s more than 3 miles we show the distance with an NSNumberFormatter
used to add a comma if more than 1,000 miles, and if the distance is
less than 3 miles, we stop updating the location and congratulate the
user on his or her reaching “the Mothership.” Listing 4 provides the complete implementation of locationManager:didUpdateLocation:fromLocation.
Listing 4.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if (newLocation.horizontalAccuracy >= 0) {
CLLocation *Cupertino = [[[CLLocation alloc]
initWithLatitude:kCupertinoLatitude
longitude:kCupertinoLongitude] autorelease];
CLLocationDistance delta = [Cupertino distanceFromLocation: newLocation];
long miles = (delta * 0.000621371) + 0.5; // meters to rounded miles
if (miles < 3) {
// Stop updating the location
[manager stopUpdatingLocation];
// Congratulate the user
distanceLabel.text = @"Enjoy the\nMothership!";
} else {
NSNumberFormatter *commaDelimited = [[[NSNumberFormatter alloc]
init] autorelease];
[commaDelimited setNumberStyle:NSNumberFormatterDecimalStyle];
distanceLabel.text = [NSString stringWithFormat:
@"%@ miles to the\nMothership",
[commaDelimited stringFromNumber:
[NSNumber numberWithLong:miles]]];
}
waitView.hidden = YES;
distanceView.hidden = NO;
}
}
|
Choose Build and Run and take a look at the result.
Your application should, after determining your location, display the
distance to Cupertino, California, as shown in Figure 4.
Did you Know?
Location services work in the iOS 4 iPhone Simulator!
The simulator uses Snow Leopard’s location services to determine where
your computer is sitting and then passes that information to your
applications.