Core Location
1. 基本对象是CLLocation,有属性coordinate, altitude, horizontal/vertical Accuracy, timestamp, speed, course
typedef {
CLLocationDegrees latitude; // a double
CLLocationDegrees longitude; // a double
} CLLocationCoordinate2D; // 经纬度
@property (readonly) CLLocationAccuracy horizontalAccuracy; // in meters
@property (readonly) CLLocationAccuracy verticalAccuracy;
kCLLocationAccuracyBestForNavigation;
kCLLocationAccuracyBest;
kCLLocationAccuracyNearestTenMeters;
kCLLocationAccuracyHundredMeters;
kCLLocationAccuracyKilometer;
kCLLocationAccuracyThreeKilometers;
- (CLLocationDistance)distanceFromLocation:(CLLocation *)otherLocation; // in meters 两个位置之间的距离
2. 总是通过CLLocationManager来获取CLLocation(通过其代理),其一般的使用方法为:
(1)检查硬件是否支持你需要的位置更新
(2)创建一个CLLocationManager实例,并设置接收更新的代理对象
(3)根据你的需求对CLLocationManager进行配置
(4)启动CLLocationManager来监视改变。
3. Core Location Manager的一些设置
@property CLLocationAccuracy desiredAccuracy; // always set this as low as possible
@property CLLocationDistance distanceFilter;
- (void)startUpdatingLocation;
- (void)stopUpdatingLocation;
- (void)startMonitoringSignificantLocationChanges;
- (void)stopMonitoringSignificantLocationChanges;
Core Location框架提供了三种用于追踪设备当前位置的服务
-
The significant-change location
service 提供了低耗电的方法来获取当前位置,当前位置改变时会发出通知 -
The standard location service 提供了一种可设置的方法来获取当前位置
-
Region monitoring 监视特定地区的跨越
Listing 1 Starting the standard location service
- (void)startStandardUpdates
{
// 创建location manager
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
// 设置获取位置的精确度,越精确越耗电
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
// 设置距离过滤器,超过次距离就更新一次位置
locationManager.distanceFilter = 500;
[locationManager startUpdatingLocation];
}
Listing 2 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];
}
4. 检查硬件
+ (BOOL)locationServicesEnabled; // has the user enabled location monitoring in Settings?
+ (BOOL)headingAvailable; // can this hardware provide heading info (compass)?
+ (BOOL)significantLocationChangeMonitoringAvailable; // only if device has cellular?
+ (BOOL)regionMonitoringAvailable; // only certain iOS4 devices
+ (BOOL)regionMonitoringEnabled; // by the user in Settings
当程序初次使用位置服务时,会询问用户。可以提供一个string来描述使用目的。如果用户拒绝,则上面所有方法均返回NO
@property (copy) NSString *purpose
5. 获取位置更新
// Delegate method from the CLLocationManagerDelegate protocol.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
// If it's a relatively recent event, turn off updates to save power
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15.0)
{
NSLog(@"latitude %+.6f, longitude %+.6f\n",
newLocation.coordinate.latitude,
newLocation.coordinate.longitude);
}
// else skip the event and process the next one.
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
typedef enum {
kCLErrorLocationUnknown = 0,
kCLErrorDenied, // 如果用户拒绝开启位置服务,那么应该停止location manager
kCLErrorNetwork,
kCLErrorHeadingFailure,
kCLErrorRegionMonitoringDenied,
kCLErrorRegionMonitoringFailure,
kCLErrorRegionMonitoringSetupDelayed,
} CLError;
6. Geocoding Location Data
Geocoder对象使用网络服务来将经纬度转换为具体地址信息,iOS当前只支持经纬度转地址信息,不能将位置信息转换为经纬度
创建一个MKReverseGeocoder实例,设置代理,调用start方法。
代理会接受到 reverseGeocoder:didFindPlacemark:和reverseGeocoder:didFailWithError:
@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
MapKit
1. MKMapView 显示地图2. 地图上可以显示注释(annotation),每个annotation由coordinate,title,subtitle构成,并由MKAnnotationView来显示
3. Annotation可以有一个callout,当annotation view被点击时显示,默认情况下只是显示title和subtitle,不过你可以添加左右accessory views
4. MKMapView显示一个遵守MKAnnotation协议的数组对象
@property (readonly) NSArray *annotations; // contains id <MKAnnotation> objects
MKAnnotation protocol @protocol MKAnnotation <NSObject> @property (readonly) CLLocationCoordinate2D coordinate; @optional @property (readonly) NSString *title; @property (readonly) NSString *subtitle; @end typedef { CLLocationDegrees latitude; CLLocationDegrees longitude; } CLLocationCoordinate2D;
5. 管理map view的annotations的方法
- (void)addAnnotation:(id <MKAnnotation>)annotation; - (void)addAnnotations:(NSArray *)annotations; - (void)removeAnnotation:(id <MKAnnotation>)annotation; - (void)removeAnnotations:(NSArray *)annotations;
6. MKMapView使用跟TableView类似的方法来重用annotation view7. Annotations通过MKAnnotationView的子类显示在地图上,默认为MKPinAnnotationView
8. 如果MKAnnotationView的canShowCallout设置为YES,那么点击会显示,同时会调用
- (void)mapView:(MKMapView *)sender didSelectAnnotationView:(MKAnnotationView *)aView; //This is a great place to set up the MKAnnotationView‘s callout accessory views lazily.
9. MKAnnotationViews的创建方法跟tableviewcell在tableview中的创建类似
- (MKAnnotationView *)mapView:(MKMapView *)sender viewForAnnotation:(id <MKAnnotation>)annotation { MKAnnotationView *aView = [sender dequeueReusableAnnotationViewWithIdentifier:IDENT]; if (!aView) { aView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:IDENT]; // set canShowCallout to YES and build aView’s callout accessory views here } aView.annotation = annotation; // yes, this happens twice if no dequeue // maybe load up accessory views here (if not too expensive)? // or reset them and wait until mapView:didSelectAnnotationView: to load actual data return aView; }
10. MKAnnotationView的一些属性
@property id <MKAnnotation> annotation; // the annotation; treat as if readonly @property UIImage *image; // instead of the pin, for example @property UIView *leftCalloutAccessoryView; // maybe a UIImageView @property UIView *rightCalloutAccessoryView; // maybe a “disclosure” UIButton @property BOOL enabled; // NO means it ignores touch events, no delegate method, no callout @property CGPoint centerOffset; // where the “head of the pin” is relative to the image @property BOOL draggable; // only works if the annotation implements setCoordinate:
11. 如果你设置一个callout accessory view为一个UIControl
e.g. aView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; The following MKMapViewDelegate method will get called when the accessory view is touched ... - (void)mapView:(MKMapView *)sender annotationView:(MKAnnotationView *)aView calloutAccessoryControlTapped:(UIControl *)control;
12. 使用一下代理方法来加载accessory views
-(void)mapView:(MKMapView *)sender didSelectAnnotationView:(MKAnnotationView *)aView { if ([aView.leftCalloutAccessoryView isKindOfClass:[UIImageView class]]) { UIImageView *imageView = (UIImageView *)aView.leftCalloutAccessoryView; imageView.image = ...; // if you do this in a GCD queue, be careful, views are reused! } }
13. 地图类型
@property MKMapType mapType;
enum { MKMapTypeStandard = 0, MKMapTypeSatellite, MKMapTypeHybrid }; typedef NSUInteger MKMapType;
14. 显示用户当前地址
@property BOOL showsUserLocation; @property (readonly) BOOL isUserLocationVisible; @property (readonly) MKUserLocation *userLocation; MKUserLocation is an object which conforms to MKAnnotation which holds the user’s location.
15.限制用户对地图的操作
@property BOOL zoomEnabled; @property BOOL scrollEnabled;
16. 控制地图的显示区域
注意:@property MKCoordinateRegion region; typedef struct { CLLocationCoordinate2D center; MKCoordinateSpan span; } MKCoordinateRegion; typedef struct { CLLocationDegrees latitudeDelta; CLLocationDegrees longitudeDelta; } - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated; // animate
你赋给region属性的值通常不是最终保存的值,在设置显示区域的时候同时也设置了缩放等级。map view不能显示任意的缩放等级,因此map view会选择一个能够尽可能显示你指定区域大小的缩放等级,然后根据此时显示的区域来保存。
@property CLLocationCoordinate2D centerCoordinate; - (void)setCenterCoordinate:(CLLocationCoordinate2D)center animated:(BOOL)animated;
17. 地图载入通知
Remember that the maps are downloaded from Google earth. - (void)mapViewWillStartLoadingMap:(MKMapView *)sender; - (void)mapViewDidFinishLoadingMap:(MKMapView *)sender; - (void)mapViewDidFailLoadingMap:(MKMapView *)sender withError:(NSError *)error;
18.