CLLocationManager
CLPlacemark 一个地点包含具体汉字和 经纬度CLLocation
CLLocation 一个点
CLRegion 一个区域
CLLocationCoordinate2D 经纬度
MKCoordinateSpan 经度方向长,维度方向宽
MKMapView
Getting the User’s Location
#import <CoreLocation/CoreLocation.h>
There are two different services you can use to get the user’s current location:
-
The standard location service is a configurable, general-purpose solution and is supported in all versions of iOS.
-
The significant-change location service offers a low-power location service for devices with cellular radios. This service is available only in iOS 4.0 and later and can also wake up an app that is suspended or not running.
Determining Whether Location Services Are Available
-
The user can disable location services in the Settings app.
-
The user can deny location services for a specific app.
-
The device might be in Airplane mode and unable to power up the necessary hardware.
locationServicesEnabled
class method of
CLLocationManager
Starting the standard location service
- (void)startStandardUpdates |
{ |
// Create the location manager if this object does not |
// already have one. |
if (nil == locationManager) |
locationManager = [[CLLocationManager alloc] init]; |
|
locationManager.delegate = self; |
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer; |
|
// Set a movement threshold for new events. |
locationManager.distanceFilter = 500; |
|
[locationManager startUpdatingLocation]; |
} |
Starting the significant-change location service
- (void)startSignificantChangeUpdates |
{ |
// Create the location manager if this object does not |
// already have one. |
if (nil == locationManager) |
locationManager = [[CLLocationManager alloc] init]; |
|
locationManager.delegate = self; |
[locationManager startMonitoringSignificantLocationChanges]; |
} |
Receiving Location Data from a Service
Whether you use the standard location service or the significant-change location service to get location events, the way you receive those events is the same. In iOS 6 and later, the location manager reports events to thelocationManager:didUpdateLocations:
method of its delegate when they become available. (In earlier versions of the system, it reports events to the locationManager:didUpdateToLocation:fromLocation:
method.) If there is an error retrieving an event, the location manager calls thelocationManager:didFailWithError:
method of its delegate instead.
Processing an incoming location event
// Delegate method from the CLLocationManagerDelegate protocol. |
- (void)locationManager:(CLLocationManager *)manager |
didUpdateLocations:(NSArray *)locations { |
// If it's a relatively recent event, turn off updates to save power |
CLLocation* location = [locations lastObject]; |
NSDate* eventDate = location.timestamp; |
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow]; |
if (abs(howRecent) < 15.0) { |
// If the event is recent, do something with it. |
NSLog(@"latitude %+.6f, longitude %+.6f\n", |
location.coordinate.latitude, |
location.coordinate.longitude); |
} |
} |
Monitoring Shape-Based Regions
In iOS, regions associated with your app are tracked at all times, including when your app is not running. If a region boundary is crossed while an app is not running, that app is relaunched into the background to handle the event. Similarly, if the app is suspended when the event occurs, it is woken up and given a short amount of time to handle the event.
Determining the Availability of Region Monitoring
Before attempting to monitor any regions, your app should check to see if region monitoring is supported on the current device. There are several reasons why region monitoring might not be available:
-
The device may not have the hardware needed to support region monitoring.
-
The user may have disabled location services in the Settings app.
-
The device might be in Airplane mode and unable to power up the necessary hardware.
For these reasons, it is recommended that you always call the regionMonitoringAvailable
andregionMonitoringEnabled
class methods of CLLocationManager
before attempting to monitor regions.
Defining a Region to Be Monitored
g a region, you must define the region and register it with the system。
Creating and registering a region based on a Map Kit overlay
- (BOOL)registerRegionWithCircularOverlay:(MyCircle*)overlay andIdentifier:(NSString*)identifier |
{ |
// Do not create regions if support is unavailable or disabled. |
if ( ![CLLocationManager regionMonitoringAvailable] || |
![CLLocationManager regionMonitoringEnabled] ) |
return NO; |
|
// If the radius is too large, registration fails automatically, |
// so clamp the radius to the max value. |
CLLocationDegrees radius = overlay.radius; |
if (radius > self.locManager.maximumRegionMonitoringDistance) |
radius = self.locManager.maximumRegionMonitoringDistance; |
|
// Create the region and start monitoring it. |
CLRegion* region = [[CLRegion alloc] initCircularRegionWithCenter:overlay.coordinate |
radius:radius identifier:identifier]; |
[self.locManager startMonitoringForRegion:region |
desiredAccuracy:kCLLocationAccuracyHundredMeters]; |
|
[region release]; |
return YES; |
} |
locationManager:monitoringDidFailForRegion:withError:
method of its delegate with the
kCLErrorRegionMonitoringFailure
error code.
Tips for Conserving Battery Power
Turn off location services when you are not using them。
Getting Direction-Related Events
Core Location supports two different ways to get direction-related information:
-
Devices with a magnetometer can report the direction in which a device is pointing, also known as its heading.
-
Devices with GPS hardware can report the direction in which a device is moving, also known as its course.
Thecourse of the device represents the direction of travel and does not take into account the device orientation.
// Start heading updates. |
if ([CLLocationManager headingAvailable]) { |
locManager.headingFilter = 5; |
[locManager startUpdatingHeading]; |
} |
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { |
if (newHeading.headingAccuracy < 0) |
return; |
|
// Use the true heading if it is valid. |
CLLocationDirection theHeading = ((newHeading.trueHeading > 0) ? |
newHeading.trueHeading : newHeading.magneticHeading); |
|
self.currentHeading = theHeading; |
[self updateHeadingDisplays]; |
} |
The actual course and speed information is returned to your app in the same CLLocation
objects you use to get the user’s position
Geocoding Location Data
Reverse geocoding is the process of converting a latitude and longitude into a placemark. Forward geocoding is the process of converting place name information into a latitude and longitude value. Reverse geocoding is supported in all versions of iOS but forward geocoding is supported only in iOS 5.0 and later.
geocoders rely on a network service,
Here are some rules of thumb for creating geocoding requests:
-
Send at most one geocoding request for any one user action.
-
If the user performs multiple actions that involve geocoding the same location, reuse the results from the initial geocoding request instead of starting individual requests for each action.
-
When you want to update the location automatically (such as when the user is moving), reissue the geocoding request only when the user's location has moved a significant distance and after a reasonable amount of time has passed. For example, in a typical situation, you should not send more than one geocoding request per minute.
-
Do not start a geocoding request at a time when the user will not see the results immediately. For example, do not start a request if your app is in the background or was interrupted and is currently in the inactive state.
In iOS, you can use either the CLGeocoder
or MKReverseGeocoder
class to handle reverse-geocoding requests. The CLGeocoder
is the preferred class to use and is available in iOS 5.0 and later. However, if your app must run on earlier versions of iOS, you can use the MKReverseGeocoder
class.
Geocoding a location using CLGeocoder
@implementation MyGeocoderViewController (CustomGeocodingAdditions) |
- (void)geocodeLocation:(CLLocation*)location forAnnotation:(MapLocation*)annotation |
{ |
if (!geocoder) |
geocoder = [[CLGeocoder alloc] init]; |
|
[theGeocoder reverseGeocodeLocation:location completionHandler: |
^(NSArray* placemarks, NSError* error){ |
if ([placemarks count] > 0) |
{ |
annotation.placemark = [placemarks objectAtIndex:0]; |
|
// Add a More Info button to the annotation's view. |
MKPinAnnotationView* view = (MKPinAnnotationView*)[map viewForAnnotation:annotation]; |
if (view && (view.rightCalloutAccessoryView == nil)) |
{ |
view.canShowCallout = YES; |
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; |
} |
} |
}]; |
} |
@end |
Geocoding a location using MKReverseGeocoder
@implementation MyGeocoderViewController (CustomGeocodingAdditions) |
- (void)geocodeLocation:(CLLocation*)location forAnnotation:(MapLocation*)annotation |
{ |
MKReverseGeocoder* theGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:location.coordinate]; |
|
theGeocoder.delegate = self; |
[theGeocoder start]; |
} |
|
// Delegate methods |
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFindPlacemark:(MKPlacemark*)place |
{ |
MapLocation* theAnnotation = [map annotationForCoordinate:place.coordinate]; |
if (!theAnnotation) |
return; |
|
// Associate the placemark with the annotation. |
theAnnotation.placemark = place; |
|
// Add a More Info button to the annotation's view. |
MKPinAnnotationView* view = (MKPinAnnotationView*)[map viewForAnnotation:annotation]; |
if (view && (view.rightCalloutAccessoryView == nil)) |
{ |
view.canShowCallout = YES; |
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; |
} |
} |
|
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error |
{ |
NSLog(@"Could not retrieve the specified place information.\n"); |
} |
@end |
|
@implementation MKMapView (GeocoderAdditions) |
|
- (MapLocation*)annotationForCoordinate:(CLLocationCoordinate2D)coord |
{ |
// Iterate through the map view's list of coordinates |
// and return the first one whose coordinate matches |
// the specified value exactly. |
id<MKAnnotation> theObj = nil; |
|
for (id obj in [self annotations]) |
{ |
if (([obj isKindOfClass:[MapLocation class]])) |
{ |
MapLocation* anObj = (MapLocation*)obj; |
|
if ((anObj.coordinate.latitude == coord.latitude) && |
(anObj.coordinate.longitude == coord.longitude)) |
{ |
theObj = anObj; |
break; |
} |
} |
} |
|
return theObj; |
} |
@end |
Converting Place Names Into Coordinates
The more information you can provide to the forward geocoder, the better the results returned to you. The geocoder object parses the information you give it and, if it finds a match, returns some number of placemark objects. The number of returned placemark objects depends greatly on the specificity of the information provided.
[geocoder geocodeAddressString:@"1 Infinite Loop" |
completionHandler:^(NSArray* placemarks, NSError* error){ |
for (CLPlacemark* aPlacemark in placemarks) |
{ |
// Process the placemark. |
} |
}]; |