In order to interact with Core Location, we need to create an instance of the Location Manager, like this: CLLocationManager *locationManager = [[CLLocationManager alloc] init]; This cr
Trang 1Where Am I?
Finding Your Way
with Core Location
our iPhone has the ability to determine where in the world it is using a frame-
work called Core Location There are actually three technologies that Core
Location can leverage to do this: GPS, cell tower triangulation, and Wi-Fi
Positioning Service (WPS) GPS is the most accurate of the three but is not
available on first-generation iPhones GPS reads microwave signals from
multiple satellites to determine the current location Cell tower triangulation
determines the current location by doing a calculation based on the locations
of the cell towers in the phone's range Cell tower triangulation can be fairly accurate in cities and other areas with a high cell tower density but becomes
less accurate in areas where there is a greater distance between towers The last option, WPS, uses the IP address from iPhone’s Wi-Fi connection to make
a guess at your location by referencing a large database of known service
providers and the areas they service WPS is imprecise and can be off by
many miles
All three methods put a noticeable drain on iPhone's battery, so keep that in mind when using Core Location Your application shouldn't poll for location any more often than is absolutely necessary When using Core Location, you
have the option of specifying a desired accuracy By carefully specifying the
absolute minimum accuracy level you need, you can prevent unnecessary
battery drain
The technologies that Core Location depends on are hidden from your appli-
cation We don't tell Core Location whether to use GPS, triangulation, or WPS.
Trang 2We just tell it how accurate we would like it to be, and it will decide which technologies give
it the best chance to fulfill your request
The Location Manager
The Core Location API is actually fairly easy to work with The main class we'll work with is
CLLocationManager, usually referred to as the Location Manager In order to interact with Core Location, we need to create an instance of the Location Manager, like this:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
This creates an instance of the Location Manager for us, but it doesn’t actually start polling for our
location We create a delegate and assign it to the Location Manager The Location Manager calls our delegate method when the location information is available This may take some time, even
a few seconds Our delegate must conform to the CLLocati onManagerDelegate protocol Setting the Desired Accuracy
After you set the delegate, you also want to set the requested accuracy As we said before, don't specify a degree of accuracy any greater than you absolutely need If you're writing
an application that just needs to know which state or country the phone is in, don’t specify
a high level of precision Remember, the more accuracy you demand of Core Location, the
more juice you're likely to use Also, keep in mind that there is no guarantee that you will get
the level of accuracy that you have requested
Here’s an example of setting the delegate and requesting a specific level of accuracy:
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
The accuracy is set using a CLLocationAccuracy value, a type that’s defined as a double The value is in meters, so if you specify a desi redAccuracy of 10, you're telling Core Location that you want it to try to determine the current location within 10 meters, if possible Specifying kCLLocationAccuracyBest, as we did previously, tells Core Location to use the most accurate
method that’s currently available In addition to kCcLLocationAccuracyBest, you can also
use kCLLocationAccuracyNearestTenMeters, kCLLocationAccuracyHundredMeters, kCLLocationAccuracyKi lometer, and kCLLocationAccuracyThreeKi lometers
Setting the Distance Filter
By default, the Location Manager will notify the delegate of any detected change in location By specifying a distance filter, you are telling Location Manager not to notify you for every change
Trang 3and to only notify you when the location changes more than a certain amount Setting up a dis-
tance filter can reduce the amount of polling that your application does Distance filters are also
set in meters Specifying a distance filter of 1000 tells the Location Manager not to notify the
delegate until the iPhone has moved at least 1,000 meters from its previously reported position Here’s an example:
locationManager.distanceFilter = 1000.0f;
If you ever want to return the Location Manager to the default setting of no filter, you can use the constant kCLDi stanceFi 1 terNone, like this:
locationManager.distanceFilter = kCLDistanceFiIlterNone;
Starting the Location Manager
When you’re ready to start polling for location, you tell the Location Manager to start, and
it will then go off and do its thing and then call a delegate method when it has determined
the current location Until you tell it to stop, it will continue to call your delegate method whenever it senses a change that exceeds the current distance filter Here’s how you start the location manager:
[locationManager startUpdatingLocation];
Using the Location Manager Wisely
If you only need to determine the current location and have no need to continuously poll
for location, you should have your location delegate stop the location manager as soon as
it gets the information your application needs If you need to continuously poll, make sure you stop polling as soon as you possibly can Remember, as long as you are getting updates from the Location Manager, you are putting a strain on the user's battery To tell the Location Manager to stop sending updates to its delegate, call stopUpdatingLocation, like this:
[locationManager stopUpdatingLocation];
The Location Manager Delegate
The location manager delegate must conform to the CLLocationManagerDe legate protocol, which defines two methods, both of which are optional One of these methods is called by the
Location Manager when it has determined the current location or when it detects a change in location The other method is called when the Location Manager encounters an error.
Trang 4Getting Location Updates
When the Location Manager wants to inform its delegate of the current location, it calls
the locationManager :didUpdateToLocation: fromLocation: method This method has
second is a CLLocation object that defines the current location of the iPhone, and the third
is a CLLocation object that defines the previous location from the last update The first time this method is called, the previous location object will be ni1
Getting Latitude and Longitude Using CLLocation
Location information is passed from the Location Manager using instances of the CLLocation
and longitude are stored in a property called coordinate To get the latitude and longitude in degrees, do this:
CLLocationDegrees latitude = theLocation.coordinate latitude;
CLLocationDegrees longitude = theLocation.coordinate longitude;
The CLLocati on object can also tell you how confident the Location Manager is in its lati-
a circle with the coordinate as its center The larger the value in horizontalAccuracy, the
less certain Core Location is of the location A very small radius indicates a high level of con-
fidence in the determined location
You can see a graphic representation of horizontalAccuracy in the Maps application (see
Figure 14-1) The blue circle shown in Maps uses horizontalAccuracy for its radius when
it detects your location The Location Manager thinks you are at the center of that circle
If you're not, you're almost certainly somewhere inside the blue circle A negative value in
horizontalAccuracy is an indication that you cannot rely on the values in coordinate for
some reason
The CLLocation object also has a property called al ti tude that can tell you how many
meters above or below sea level you are:
CLLocationDistance altitude = theLocation.altitude
Each CLLocation object maintains a property called verticalAccuracy that is an indication
of how confident Core Location is in its determination of altitude The value in altitude could
be off by as many meters as the value in verticalAccuracy, and if the verticalAccuracy value is negative, Core Location is telling you it could not determine a valid altitude
CLLocation objects also have a timestamp that tells when the Location Manager made the location determination.
Trang 5
In addition to these properties, CLLocation also has a use-
ful instance method that will let you determine the distance
between two CLLocation objects The method is called
CLLocationDistance distance = [fromLocation=™
getDistanceFrom:toLocation] ;
The preceding line of code will return the distance between
two CLLocation objects, fromLocation and toLocation
distance calculation that ignores the altitude property and cal-
culates the distance as if both points were at sea level For most
purposes, a great-circle calculation will be more than sufficient,
but if you do need to take altitude into account when calculat-
Figure 14-1 The Maps appli- cation uses Core Location to
If Core Location is not able to determine your current loca- determine your current loca-
tion, it will call a second delegate method named location tion The blue circle is a visual
error is that the user denies access Location Manager use has
to be authorized by the user, so the first time your application
goes to determine the location, an alert will pop up on the
screen asking the user if it’s OK for the current program
to access your location (see Figure 14-2)
Error Notifications
If the user clicks the Don't Allow button, your delegate will
be notified of the fact by the Location Manager using the
locationManager:didFailwithError: with an error code
‘WhereAm!” would like to use
other error code supported by Location Manager is si ei cà
kCLErrorLocationUnknown, which indicates that Core
Location was unable to determine the location but that it
will keep trying The kCcLErrorDenied error generally indi-
cates that your application will not be able to access Core
Location any time during the remainder of the current ses-
sion On the other hand, kCLErrorLocationUnknown errors
indicate a problem that may be temporary
Figure 14-2 Location Manager access has to be approved by the user
Trang 6OTE
When working in the simulator, you will not be prompted for access to Core Location, and location will be determined using a super secret algorithm kept in a locked vault buried deep beneath Apple headquarters
in Cupertino
Trying Out Core Location
Let’s build a small application to detect the iPhone's current
location and the total distance traveled while the program
Horizontal Accuracy:
application template, and call the project WhereAml
Expand the Classes and Resources folders, and single-click
WhereAm|ViewController.h Make the following changes,
which we'll discuss in a moment:
Distance Traveled:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@interface WhereAmIViewControl ler
UIViewController <CLLocationManagerDelegate> {
CLLocat ionManager *locationManager; Figure 14-3 The WhereAm|
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
CLLocation *startingPoint;
TBOutTet UILabel *latitudeLabel ;
TBOutTet UILabel *longitudeLabel ;
TBOutTet UILabel *horizontalAccuracyLabel ;
TBOutTet UILabel *altitudeLabel ;
TBOutTet UTLabeT *verticaTlAccuracyLabel ;
TBOutTet UILabel *distanceTraveledLabel ;
}
@property (retain, nonatomic) CLLocationManager *locationManager ;
@property (retain, nonatomic) CLLocation *startingPoint ;
@property (retain, nonatomic) UILabel *latitudeLabel ;
@property (retain, nonatomic) UILabel *longitudeLabel ;
@property (retain, nonatomic) UILabel *horizontalAccuracyLabel;
@property (retain, nonatomic) UILabel *altitudeLabel ;
@property (retain, nonatomic) UILabel *verticalAccuracyLabel;
@property (retain, nonatomic) UILabel *distanceTraveled;
@end
Trang 7The 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 CLLocati onManagerDelegate 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 CLLocati on, 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 an update, we'll be able to calcu- late how far our user moved
The remaining instance variables are all outlets that will be used to update labels on the user interface
Double-click WhereAm!ViewController.xib to open Interface Builder Using Figure 14-3 as your guide, drag twelve Labels over 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 The six bold labels should be given the values Latitude:, Longitude:, Horizontal Accuracy, Altitude:, Vertical Accu-
racy:, 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 appropri- ate 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 WhereAm!ViewController.m, and make the following changes:
#import "WhereAmIViewController.h"
@implementation WhereAmIViewController
@synthesize locationManager ;
@synthesize startingPoint;
@synthesize latitudeLabel;
@synthesize longitudeLabel ;
@synthesize horizontalAccuracyLabel ;
@synthesize altitudeLabel;
@synthesize verticalAccuracyLabel ;
@synthesize distanceTraveled;
#pragma mark -
- (void)viewDidLoad {
self.locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest ;
[locationManager startUpdatingLocation] ;
}
- (BOOL) shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
Trang 8// Return YES for supported orientations
return CinterfaceOrientation == UIInterfaceOrientationPortrait);
(void) didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
(void)dealloc {
[locationManager release];
[startingPoint release];
[latitudeLabel release];
LlongitudeLabel release];
[horizontalAccuracyLabel release];
[altitudeLabel release];
[verticalAccuracyLabel release];
[distanceTraveled release] ;
[super dealloc];
}
#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;
LlongitudeString release];
NSString *horizontalAccuracyString = [[NSString alloc]
initWithFormat:@"%gm",
newLocation.horizontal Accuracy];
horizontalAccuracyLabel.text = horizontalAccuracyString;
[horizontalAccuracyString release];
NSString *altitudeString = [[NSString alloc] initWithFormat:@"%gm",
Trang 9newLocation.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 CLLocati onManager 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] ;
Trang 10Updating Location Manager
Since this class designated itself as the location manager’s delegate, we know that location updates will come in to this class if we implement the delegate method locationmanager:
di dUpdateToLocation: fromLocation:, so let’s look at our implementation of that method
The first thing we do in that method is check to see if startingPoint is ni 1 If it is, then this update is the first one from the Location Manager, and we assign the current location to our
startingPoint property
if CstartingPoint == nil)
self.startingPoint = newLocation;
After that, we update the first six labels with values from the CLLocation object passed in
the newLocation argument:
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;
LlongitudeString release];
NSString *horizontalAccuracyString = [[NSString alloc]
ini twithFormat:@"%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]
ini twithFormat:@"%gm" ,
newLocation.verticalAccuracy];
verticalAccuracyLabel.text = verticalAccuracyString;
[verticalAccuracyString release] ;
TIP
You can type the degree symbol (°) by pressing <> \8