Let’s build a small application to detect the iPhone’s current location and the total distance traveled while the program has been running. You can see what our final application will look like in Figure 14-3.
Figure 14-3. The WhereAmI application in action.
This screenshot was taken in the simulator. Notice that the vertical accuracy is a negative number, which tells us it couldn’t determine the altitude.
In Xcode, create a new project using the view-based application template, and call the project WhereAmI. Expand the Classes and Resources folders, and single-click
WhereAmIViewController.h. Make the following changes, which we’ll discuss in a moment:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@interface WhereAmIViewController :
UIViewController <CLLocationManagerDelegate> { CLLocationManager *locationManager;
CLLocation *startingPoint;
UILabel *latitudeLabel;
UILabel *longitudeLabel;
UILabel *horizontalAccuracyLabel;
UILabel *altitudeLabel;
UILabel *verticalAccuracyLabel;
UILabel *distanceTraveledLabel;
}
@property (retain, nonatomic) CLLocationManager *locationManager;
@property (retain, nonatomic) CLLocation *startingPoint;
@property (retain, nonatomic) IBOutlet UILabel *latitudeLabel;
@property (retain, nonatomic) IBOutlet UILabel *longitudeLabel;
@property (retain, nonatomic) IBOutlet UILabel *horizontalAccuracyLabel;
@property (retain, nonatomic) IBOutlet UILabel *altitudeLabel;
@property (retain, nonatomic) IBOutlet UILabel *verticalAccuracyLabel;
@property (retain, nonatomic) IBOutlet UILabel *distanceTraveledLabel;
@end
The first thing to notice is that we’ve included the Core Location header files. Core Location is not part of the UIKit, so we need to include the header files manually. Next, we conform this class to the CLLocationManagerDelegate method so that we can receive location infor- mation from the Location Manager.
After that, we declare a CLLocationManager pointer, which will be used to hold the instance of the Core Location we create. We also declare a pointer to a CLLocation, which we will set to the location we receive in the first update from the Location Manager. This way, if the user has our program running and moves far enough to trigger updates, we’ll be able to calculate how far our user moved. Our delegate will be notified of the previous location with each call, but not the original starting location, which is why we store it.
The remaining instance variables are all outlets that will be used to update labels on the user interface.
Double-click WhereAmIViewController.xib to open Interface Builder. Using Figure 14-3 as your guide, drag 12 Labels from the library to the View window. Six of them should be placed on the left side of the screen, right justified, and made bold. Give the six bold labels the values Latitude:, Longitude:, Horizontal Accuracy:, Altitude:, Vertical Accuracy:, and Distance Traveled:.
The other six should be left justified and placed next to each of the bold labels. Each of the
labels on the right side should be connected to the appropriate outlet we defined in the header file earlier. Once you have all six attached to outlets, double-click each one in turn, and delete the text it holds. Save and go back to Xcode.
Single-click WhereAmIViewController.m, and make the following changes at the top of the file:
#import "WhereAmIViewController.h"
@implementation WhereAmIViewController
@synthesize locationManager;
@synthesize startingPoint;
@synthesize latitudeLabel;
@synthesize longitudeLabel;
@synthesize horizontalAccuracyLabel;
@synthesize altitudeLabel;
@synthesize verticalAccuracyLabel;
@synthesize distanceTraveledLabel;
#pragma mark -
- (void)viewDidLoad {
self.locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
} ...
Insert the following lines in viewDidUnload and dealloc to clean up our outlets:
...
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.locationManager = nil;
self.latitudeLabel = nil;
self.longitudeLabel = nil;
self.horizontalAccuracyLabel = nil;
self.altitudeLabel = nil;
self.verticalAccuracyLabel = nil;
self.distanceTraveledLabel= nil;
[super viewDidUnload];
}
- (void)dealloc {
[locationManager release];
[startingPoint release];
[latitudeLabel release];
[longitudeLabel release];
[horizontalAccuracyLabel release];
[altitudeLabel release];
[verticalAccuracyLabel release];
[distanceTraveledLabel release];
[super dealloc];
} ...
And insert the following new methods at the end of the file:
...
#pragma mark -
#pragma mark CLLocationManagerDelegate Methods
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if (startingPoint == nil)
self.startingPoint = newLocation;
NSString *latitudeString = [[NSString alloc] initWithFormat:@"%g°", newLocation.coordinate.latitude];
latitudeLabel.text = latitudeString;
[latitudeString release];
NSString *longitudeString = [[NSString alloc] initWithFormat:@"%g°", newLocation.coordinate.longitude];
longitudeLabel.text = longitudeString;
[longitudeString release];
NSString *horizontalAccuracyString = [[NSString alloc]
initWithFormat:@"%gm",
newLocation.horizontalAccuracy];
horizontalAccuracyLabel.text = horizontalAccuracyString;
[horizontalAccuracyString release];
NSString *altitudeString = [[NSString alloc] initWithFormat:@"%gm", newLocation.altitude];
altitudeLabel.text = altitudeString;
[altitudeString release];
NSString *verticalAccuracyString = [[NSString alloc]
initWithFormat:@"%gm",
newLocation.verticalAccuracy];
verticalAccuracyLabel.text = verticalAccuracyString;
[verticalAccuracyString release];
CLLocationDistance distance = [newLocation getDistanceFrom:startingPoint];
NSString *distanceString = [[NSString alloc]
initWithFormat:@"%gm", distance];
distanceTraveledLabel.text = distanceString;
[distanceString release];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSString *errorType = (error.code == kCLErrorDenied) ? @"Access Denied" : @"Unknown Error";
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Error getting Location"
message:errorType delegate:nil cancelButtonTitle:@"Okay"
otherButtonTitles:nil];
[alert show];
[alert release];
}
@end
In the viewDidLoad method, we allocate and initialize a CLLocationManager instance, assign our controller class as the delegate, set the desired accuracy to the best available, and then tell our Location Manager instance to start giving us location updates:
- (void)viewDidLoad {
self.locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}