定位
定位功能
- 定位主要用于获取用户当前地理位置信息。
- 常用的定位技术:
GPS定位(全球定位系统:最精准的定位方式);
Skyhook Wi-Fi定位(Wi-Fi路由器);
因特网提供商定位技术(提供商中心站);
多种定位方法混合使用; - Core Location是iOS中实现定位功能的框架,提供了大量接口对定位功能进行配置及获取地理位置数据。
- 使用Core Location需要导入框架:CoreLocation.framework。
- CoreLocation提供了CLLocationManager类来对定位功能进行管理,包括定位功能的配置、开始、停止定位等。
- CoreLocation也提供了CLLocationManagerDelegate委托协议来处理CLLocationManager在定位过程中触发的事件。
CLLocationManager 工作流程
- CLLocationManager 定位授权
iOS8以后,如果需要使用定位功能,需请求用户授权,在首次运行时会弹框提示,方法如下:
// 1、请求在使用期间授权定位,需在Info.plist文件中加入字段:NSLocationWhenInUseUsageDescription
- (void)requestWhenInUseAuthorization;
// 2、请求总是授权定位,需在Info.plist文件中加入字段:NSLocationAlwaysUsageDescription
- (void)requestAlwaysAuthorization;
- CLLocationManager 常用属性
delegate:设置代理
distanceFilter:设置定位频率,每隔多少米定位一次
desiredAccuracy:设置定位精度(kCLLocationAccuracy)
CLLocationManager 常用方法
类方法
// 1、是否启用定位服务,通常如果用户没有启用定位服务可以提示用户打开定位服务 + (BOOL)locationServicesEnabled; // 2、定位服务授权状态,返回枚举类型: + (CLAuthorizationStatus)authorizationStatus;
- 实例化方法
// 1、开始定位追踪
- (void)startUpdatingLocation;
// 2、停止定位追踪
- (void)stopUpdatingLocation;
// 3、开始导航方向追踪
- (void)startUpdatingHeading;
// 4、停止导航方向追踪
- (void)stopUpdatingHeading;
- 代理方法
// 1、定位失败 - (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
// 2、位置发生改变后执行(第一次定位到某个位置之后也会执行)
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations;
// 3、导航方向发生变化后执行
- (void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)newHeading;
// 4、进入某个区域之后执行
- (void)locationManager:(CLLocationManager *)manager
didEnterRegion:(CLRegion *)region;
// 5、走出某个区域之后执行
- (void)locationManager:(CLLocationManager *)manager
didExitRegion:(CLRegion *)region;
CLLocationManager 定位代码示例
#import "ViewController.h"
// 定位框架
#import
#define NSLOG(OBJECT) NSLog(@"%@", OBJECT)
@interfaceViewController ()
{
CLLocationManager *_locationManager; /**< 定位服务 */
}
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1、判断设备是否开启定位服务
if (![CLLocationManager locationServicesEnabled]) {
// 弹框提示
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"您的设备暂未开启定位服务!" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
return;
}
// 2、初始化定位服务
_locationManager = [[CLLocationManager alloc] init];
// 3、请求定位授权
// 请求在使用期间授权(弹框提示用户是否允许在使用期间定位),需添加NSLocationWhenInUseUsageDescription到info.plist
[_locationManager requestWhenInUseAuthorization];
// 请求在后台定位授权(弹框提示用户是否允许不在使用App时仍然定位),需添加NSLocationAlwaysUsageDescription添加key到info.plist
[_locationManager requestAlwaysAuthorization];
// 4、设置定位精度
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// 5、设置定位频率,每隔多少米定位一次
_locationManager.distanceFilter = 10.0;
// 6、设置代理 _locationManager.delegate = self;
// 7、开始定位
// 注意:开始定位比较耗电,不需要定位的时候最好调用 [stopUpdatingLocation] 结束定位。
[_locationManager startUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate methods
// 定位失败
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLOG(error.localizedDescription);
}
// 位置更新
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
// 获取最新定位
CLLocation *location = locations.lastObject;
// 打印位置信息
NSLog(@"精度:%.2f, 纬度:%.2f", location.coordinate.latitude, location.coordinate.longitude);
// 停止定位
[_locationManager stopUpdatingLocation];
}
@end
- 注意:
1、定位频率和定位精度并不应当越精确越好,需要视实际情况而定,因为越精确越耗性能,也就越费电。
2、定位成功后会根据设置情况频繁调用didUpdateLocations:方法,这个方法返回一组地理位置对象数组,每个元素是一个CLLocation,代表地理位置信息(包含经度、纬度、海报、行走速度等信息),之所以返回数组是因为有些时候一个位置点可能包含多个位置。
3、使用完定位服务后如果不需要实时监控应该立即关闭定位服务以节省资源。
4、除了提供定位功能,CLLocationManager还可以调用startMonitoringForRegion:方法对指定区域进行监控。
地理编码
除了提供位置跟踪功能之外,在定位服务中还包含CLGeocoder 类用于处理地理编码和逆地理编码(又叫反地理编码)功能。
地理编码:根据给定的位置(通常是地名)确定地理坐标(经、纬度)。
逆地理编码:可以根据地理坐标(经、纬度)确定位置信息(街道、门牌等)。
- 地理编码方法
// 1、地理编码
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
// 2、逆地理编码
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
- 地理编码示例
#import "ViewController.h"
#import
@interface ViewController ()
{
CLGeocoder *_geocoder;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_geocoder = [[CLGeocoder alloc] init];
[self getCoordinateByAddress:@"成都"];
[self getAddressByLatitude:39.54 longitude:116.28];
}
#pragma mark - 根据地名确定地理坐标
- (void)getCoordinateByAddress:(NSString *)address {
// 地里编码
[_geocoder geocodeAddressString:address completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
// 取得第一个地标,地标中存储了详细的地址信息
// 注意:一个地名可能搜索出多个地址
CLPlacemark *placemark = placemarks.firstObject;
// 位置 CLLocation *location = placemark.location;
// 区域 CLRegion *region = placemark.region;
// 详细地址信息字典
NSDictionary *addressDic = placemark.addressDictionary;
NSLog(@"\n位置:%@\n区域:%@\n详细信息:%@\n", location, region, addressDic);
/*
NSString *name = placemark.name;//地名
NSString *thoroughfare = placemark.thoroughfare;//街道
NSString *subThoroughfare = placemark.subThoroughfare;//街道相关信息,例如门牌等
NSString *locality = placemark.locality;// 城市
NSString *subLocality = placemark.subLocality;// 城市相关信息,例如标志性建筑
NSString *administrativeArea = placemark.administrativeArea;// 州
NSString *subAdministrativeArea = placemark.subAdministrativeArea;//其他行政区域信息
NSString *postalCode = placemark.postalCode;//邮编
NSString *ISOcountryCode = placemark.ISOcountryCode;//国家编码
NSString *country = placemark.country;//国家
NSString *inlandWater = placemark.inlandWater;//水源、湖泊
NSString *ocean = placemark.ocean;// 海洋
NSArray *areasOfInterest = placemark.areasOfInterest;//关联的或利益相关的地标
*/
}];
}
#pragma mark - 根据坐标取得地名
- (void)getAddressByLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude {
// 反地理编码
CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
[_geocoder reverseGeocodeLocation:location completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
if (error) {
NSLog(@"%@", error.localizedDescription);
}else {
CLPlacemark *placemark=[placemarks firstObject];
NSLog(@"详细信息:%@",placemark.addressDictionary);
}
}];
}
@end
- 注意:地理编码和逆地理编码不可同时执行;
地图
iOS从6.0开始地图数据不再由谷歌驱动,而是改用自家地图,当然在国内它的数据是由高德地图提供的。这样一来,如果在iOS6.0之前进行地图开发的话使用方法会有所不同,基于目前的情况其实使用iOS6.0之前版本的系统基本已经寥寥无几了,所有在接下来的内容中不会再针对iOS5及之前版本的地图开发进行介绍。
在iOS中进行地图开发主要有两种方式,一种是直接利用MapKit框架进行地图开发,利用这种方式可以对地图进行精准的控制;另一种方式是直接调用苹果官方自带的地图应用,主要用于一些简单的地图应用(例如:进行导航覆盖物填充等),无法进行精确的控制。当然,本节重点内容还是前者,后面的内容也会稍加提示。
Map Kit框架中 MKMapView类 提供了系统自带的地图界面效果,配合Core Location的使用可以展示多样化的信息。
使用Map Kit需要导入框架:MapKit.framework
MKMapView 常用属性
showsUserLocation:设置是否显示用户位置
userTrackingMode:设置跟踪类型,枚举类型
MKUserTrackingModeNone:不进行用户位置跟踪
MKUserTrackingModeFollow:跟踪用户位置
MKUserTrackingModeFollowWithHeading:跟踪用户位置并且跟踪用户前进方向
showsTraffic:设置是否显示交通(ios9新特性)
showsScale:设置是否显示比例(ios9新特性)
showsCompass:设置是否显示指南针(ios9新特性)
userLocation:用户位置信息,只读属性
userLocation.title:设置用户位置信息标题
userLocation.subtitle:设置用户位置信息子标题
annotations:当前地图中的所有大头针,只读属性
mapType:设置地图类型
MKMapTypeStandard:标准地图,一般情况下使用此地图即可满足
MKMapTypeSatellite:卫星地图
MKMapTypeHybrid:混合地图,加载最慢比较消耗资源
MKMapTypeSatelliteFlyover:iOS9新特性,卫星地图,支持城市观光
MKMapTypeHybridFlyover:iOS9新特性,混合地图,支持城市观光
MKMapView 常用方法
- 初始化方法
- (instancetype)initWithFrame:(CGRect)frame;
- 实例化方法
// 1、添加大头针,对应的有添加大头针数组
- (void)addAnnotation:(id )annotation;
// 2、删除大头针,对应的有删除大头针数组
- (void)removeAnnotation:(id )annotation;
// 3、设置地图显示区域,用于控制当前屏幕显示地图范围
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;
// 4、设置地图中心点位置
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
// 5、将地理坐标(经纬度)转化为数学坐标(UIKit坐标)CGPoint
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable UIView *)view;
// 6、将数学坐标CGPoint转换为地理坐标
- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view;
// 7、从缓存池中取出大头针,类似于UITableView中取出UITableViewCell,为了进行性能优化而设计
- (nullable MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier;
// 8、选中指定的大头针
- (void)selectAnnotation:(id )annotation animated:(BOOL)animated;
// 9、取消选中指定的大头针
- (void)deselectAnnotation:(nullable id )annotation animated:(BOOL)animated;
- 代理方法
// 1、用户位置发生改变时触发(第一次定位到用户位置也会触发该方法)
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;
// 2、地图加载完成后触发
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView;
// 3、显示大头针时触发,返回大头针视图,通常自定义大头针可以通过此方法进行
- (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation;
// 4、点击选中某个大头针时触发
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view;
// 5、取消选中大头针时触发
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view;
// 6、渲染地图覆盖物时触发
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay
用户位置跟踪
-
在很多带有地图的应用中默认打开地图都会显示用户当前位置,同时将当前位置标记出来放到屏幕中点方便用户对周围情况进行查看。如果在iOS6或者iOS7中实现这个功能只需要添加地图控件、设置用户跟踪模式、在didUpdateUserLocation:代理方法中设置地图中心区域及显示范围。但是在iOS8中用法稍有不同:
由于在地图中进行用户位置跟踪需要使用定位功能,而定位功能在iOS8中设计发生了变化,因此必须按照前面定位章节中提到的内容进行配置和请求。 iOS8中不需要进行中心点的指定,默认会将当前位置设置中心点并自动设置显示区域范围。
了解以上两点,要进行用户位置跟踪其实就相当简单了,值得一提的是didUpdateUserLocation:这个代理方法。这个方法只有在定位到当前位置之后就会调用,以后每当用户位置发生改变就会触发,调用频率相当频繁。
大头针
- 在iOS开发中经常会标记某个位置,需要使用地图标注,也就是大家俗称的“大头针”。只要一个NSObject类实现MKAnnotation协议就可以作为一个大头针,通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性,然后在程序中创建大头针对象并调用addAnnotation:方法添加大头针即可(之所以iOS没有定义一个基类实现这个协议供开发者使用,多数原因应该是MKAnnotation是一个模型对象,对于多数应用模型会稍有不同,例如后面的内容中会给大头针模型对象添加其他属性)。下面代码示例中,实现通过长按手势,添加大头针逻辑。
- (void)respondsToGesture:(UILongPressGestureRecognizer *)gesture { // 当长按手势开始时,添加一个标注数据源if (gesture.state == UIGestureRecognizerStateBegan) { // 获取用户长按手势在地图上的点CGPoint point = [gesture locationInView:self.mapView]; // 将地图上的点转化为经纬度 CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView]; // 创建一个标注数据源,这里使用系统标注数据源:MKPointAnnotation,如果要自定义,必须遵守协议 MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init]; // 配置标注数据源 annotation.coordinate = coordinate;
annotation.title = @"标注位置";
annotation.subtitle = [NSString stringWithFormat:@"经度:%.2f, 纬度:%.2f", coordinate.longitude, coordinate.latitude]; // 在地图上添加标注 [self.mapView addAnnotation:annotation];
}
}
- MKAnnotationView 标注视图
在一些应用中系统默认的大头针样式可能无法满足实际的需求,此时就需要修改大头针视图默认样式。根据前面MapKit的代理方法不难发现viewForAnnotation:方法可以返回一个大头针视图,只要实现这个方法并在这个方法中定义一个大头针视MKAnnotationView对象并设置相关属性就可以改变默认大头针的样式
MKPinAnnotationView
MKPinAnnotationView为MKAnnotationView子类,其特有属性如下:
pinTintColor:设置大头针前景色(iOS9新特性)
animatesDrop:设置大头针凋零效果
- MKAnnotationView 常用属性
image:设置大头针图片
selected:是否被选中状态
annotation:大头针模型信息,包括标题、子标题、地理位置
calloutOffset:点击大头针时弹出详情信息视图的偏移量
leftCalloutAccessoryView:弹出详情左侧视图
rightCalloutAccessoryView:弹出详情右侧视图
canShowCallout:点击大头针是否显示标题、子标题内容等,注意如果在viewForAnnotation:方法中重新定义大头针默认情况是无法交互的,需要设其值为true。
- 注意:
1、这个代理方法的调用时机:每当有大头针显示到系统可视界面中时就会调用此方法返回一个大头针视图放到界面中,同时当前系统位置标注(也就是地图中蓝色的位置点)也是一个大头针,也会调用此方法,因此处理大头针视图时需要区别对待。
2、类似于UITableView的代理方法,此方法调用频繁,开发过程中需要重复利用MapKit的缓存池将大头针视图缓存起来重复利用。
3、自定义大头针默认情况下不允许交互,如果交互需要设置canShowCallout = true。
4、如果代理方法返回nil则会使用默认大头针视图,需要根据情况设置。
- 下面例子进行了标注视图的自定义,这里设置了大头针的弹出视图、大头针颜色以及偏移量等信息。
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
// MKPinAnnotationView:MKAnnotationView子类,可设置大头针颜色pinTintColor(iOS9)
// 由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
static NSString * kMKPinAnnotationViewIdentifier = @"identifier";
// 大头针重用
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:kMKPinAnnotationViewIdentifier];
if (!annotationView) {
// 如果缓存池中不存在则新建
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:kMKPinAnnotationViewIdentifier];
}
// 修改大头针视图
// 重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置)
annotationView.annotation = annotation;
// 设置大头针图片
annotationView.image = [UIImage imageNamed:@"pin"];
// 设置大头针凋零效果
annotationView.animatesDrop = YES;
// 允许用户交互点击(弹框显示标注详情)
annotationView.canShowCallout = YES;
// 定义详情视图偏移量
annotationView.calloutOffset = CGPointMake(0, 1);
// 设置大头针颜色
annotationView.pinTintColor = [UIColor blueColor];
// 自定义大头针详情右侧视图
UIButton *navigationBtn = [UIButton buttonWithType:UIButtonTypeCustom];
navigationBtn.bounds = CGRectMake(0, 0, 100, 60);
navigationBtn.backgroundColor = [UIColor grayColor];
[navigationBtn setTitle:@"导航" forState:UIControlStateNormal];
annotationView.rightCalloutAccessoryView = navigationBtn;
return annotationView;
}
导航
在自定义标注视图上添加导航按钮(这里需说明:如果添加在标注视图上的控件继承于UIControl,无需添加事件),在点击导航按钮时会自动触发calloutAccessoryControlTapped:方法,如下将实现地图导航路线绘制关键代码。
效果展示
- 代码示例
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
// 导航
// 异常处理,如果标注数据源标题为空,则直接retun。
if ([view.annotation.title isKindOfClass:[NSNull class]]) {
return;
}
// 创建两个PlaceMark
// MKPlacemark,定位框架中地标类,封装了详细的地理信息
MKPlacemark *currentPlaceMark = [[MKPlacemark alloc] initWithCoordinate:mapView.userLocation.coordinate addressDictionary:nil];
MKPlacemark *destinationPlaceMark = [[MKPlacemark alloc] initWithCoordinate:((MKPointAnnotation *)view.annotation).coordinate addressDictionary:nil];
// 创建两个MapItem
MKMapItem *currentItem = [[MKMapItem alloc] initWithPlacemark:currentPlaceMark];
MKMapItem *destinationItem = [[MKMapItem alloc] initWithPlacemark:destinationPlaceMark];
// 创建导航请求
MKDirectionsRequest *directionRequest = [[MKDirectionsRequest alloc] init];
// 配置导航请求
// 1、起点
directionRequest.source = currentItem;
// 2、终点
directionRequest.destination = destinationItem;
// 发送导航请求
MKDirections *derections = [[MKDirections alloc] initWithRequest:directionRequest];
// 获取导航路线
[derections calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
// 添加一条路线(这里不会显示,需要渲染路线)
// MKRoute 路径信息
// polyLine 路线
[_mapView addOverlay:((MKRoute *)response.routes[0]).polyline];
}];
// 设置交通类型
directionRequest.transportType = MKDirectionsTransportTypeAutomobile;
// 计算被请求的交通时间信息
[directions calculateETAWithCompletionHandler:^(MKETAResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"预计进行时间:%.2f", response.expectedTravelTime);
}];
// 值得一提的是,即使实现了上述逻辑,导航路线也并不会被显示出来,此时需要实现如下方法,对路线进行渲染。
}
// 路线渲染
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay {
// 初始化路线渲染器
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
// 设置路线宽度
renderer.lineWidth = 2.0;
// 设置路线颜色
renderer.strokeColor = [UIColor redColor];
return renderer;
}
系统自带地图导航
- 要使用地图导航功能在自带地图应用中相当简单,只要设置参数配置导航模式即可。
- 效果展示
- 代码示例
#import "ViewController.h"
#import
#import
@interface ViewController ()
{
CLGeocoder *_geocoder;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_geocoder=[[CLGeocoder alloc]init];
[self startNavigation];
}
- (void)startNavigation {
//根据“成都市”地理编码
[_geocoder geocodeAddressString:@"成都市" completionHandler:^(NSArray *placemarks, NSError *error) {
//获取第一个地标
CLPlacemark *clPlacemark1 = [placemarks firstObject];
MKPlacemark *mkPlacemark1 = [[MKPlacemark alloc]initWithPlacemark:clPlacemark1];
//注意地理编码一次只能定位到一个位置,不能同时定位,所在放到第一个位置定位完成回调函数中再次定位
[_geocoder geocodeAddressString:@"重庆市" completionHandler:^(NSArray *placemarks, NSError *error) {
// 获取第一个地标
CLPlacemark *clPlacemark2 = [placemarks firstObject];
MKPlacemark *mkPlacemark2 = [[MKPlacemark alloc]initWithPlacemark:clPlacemark2];
NSDictionary *options = @{MKLaunchOptionsMapTypeKey : @(MKMapTypeStandard),
MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
// MKMapItem *mapItem1 =[MKMapItem mapItemForCurrentLocation];
//当前位置
MKMapItem *mapItem1=[[MKMapItem alloc]initWithPlacemark:mkPlacemark1];
MKMapItem *mapItem2=[[MKMapItem alloc]initWithPlacemark:mkPlacemark2];
// 打开导航地图
[MKMapItem openMapsWithItems:@[mapItem1, mapItem2] launchOptions:options];
}];
}];
}
@end
特别说明
- 由于定位和地图框架中用到了诸多类,有些初学者容易混淆,下面简单对比一下。
CLLocation:用于表示位置信息,包含地理坐标、海拔等信息,包含在CoreLoaction框架中。
MKUserLocation:一个特殊的大头针,表示用户当前位置。
CLPlacemark:定位框架中地标类,封装了详细的地理信息。
MKPlacemark:类似于CLPlacemark,只是它在MapKit框架中,可以根据CLPlacemark创建MKPlacemark。