操作系统 11

iOS进阶学习,地图与定位

iOS进阶学习-地图,ios进阶地图

一、地图的简介

  • 在移动互联网时代,移动app能解决用户的很多生活琐事,比如:
  • 导航:去任意陌生的地方。
  • 周边:找餐馆、找酒店、找银行、找电影院。
  • 手机软件:微信摇一摇、QQ附近的人、微博、支付宝等。
  • 在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这两大功能,必须基于两个框架进行开发:
  • Map Kit :用于地图展示。
  • Core Location :用于地理定位。

二、地图定位(CoreLocation框架,地理编码与反地理编码)

  1、CoreLocation框架的使用

  • 导入头文件:#import <CoreLocation/CoreLocation.h>
  • 注意:
  • CoreLocation框架中所有数据类型的前缀都是CL
  • CoreLocation中使用CLLocationManager对象来做用户定位

  2、用户隐私的保护

  • 从iOS
    7之后,苹果在保护用户隐私方面做了很大的加强,以下操作都必须经过用户批准授权:
  • ①要想获得用户的位置和访问用户的通讯录、日历、相机、相册等等都需要用户来手动授权。
  • ②当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户授权。
  • 操作系统 1
  • 开发者可以在Info.plist中设置NSLocationUsageDescription说明定位的目的(Privacy –
    Location Usage Description),一旦用户选择了“Don’t
    Allow”,意味着你的应用以后就无法使用定位功能,为了严谨起见,最好在使用定位功能之前判断当前应用的定位功能是否可用。
  • CLLocationManager有个类方法可以判断当前应用的定位功能是否可用:+
    (BOOL)locationServicesEnabled;
  • 操作系统 2
  • 代码示例:

// 引入库的头文件
#import <CoreLocation/CoreLocation.h>
// 遵守协议
@interface ViewController ()<CLLocationManagerDelegate>
/// 定位管理器
@property (nonatomic, strong) CLLocationManager *manager;

@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // 定位的步骤
    // 第一步:初始化定位管理器
    self.manager = [[CLLocationManager alloc] init];
    // 第二步:进行隐私的判断并授权
    // 进行隐私的判断
    if (![CLLocationManager locationServicesEnabled
         ]) {
        NSLog(@"是否前往隐私");
    }
    // 根据状态进行授权(系统当前版本的判断)
    if ([UIDevice currentDevice].systemVersion.integerValue >= 8.0) {
        // 判断授权状态
        /*
         定位服务授权状态,返回枚举类型:
         kCLAuthorizationStatusNotDetermined: 用户尚未做出决定是否启用定位服务
         kCLAuthorizationStatusRestricted: 没有获得用户授权使用定位服务,可能用户没有自己禁止访问授权
         kCLAuthorizationStatusDenied :用户已经明确禁止应用使用定位服务或者当前系统定位服务处于关闭状态
         kCLAuthorizationStatusAuthorizedAlways: 应用获得授权可以一直使用定位服务,即使应用不在使用状态
         kCLAuthorizationStatusAuthorizedWhenInUse: 使用此应用过程中允许访问定位服务
         */
        if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
            // 在授权请求之前需要在info.plist中设置允许定位的内容:NSLocationWhenInUseUsageDescription
            // 请求授权
            [self.manager requestWhenInUseAuthorization];
        }
    }
    // 第三步:设置管理器的代理
    self.manager.delegate = self;

    // 设置精确度CLLocationAccuracy 是一个枚举值
  /*
  最佳导航
    kCLLocationAccuracyBestForNavigation 
  最精准
     kCLLocationAccuracyBest;
  10米
     kCLLocationAccuracyNearestTenMeters; 
  百米
   kCLLocationAccuracyHundredMeters; 
  千米
     kCLLocationAccuracyKilometer; 
  3千米
     kCLLocationAccuracyThreeKilometers;     
  */
    self.manager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    // 设置最小更新距离(单位:米)
    self.manager.distanceFilter = 10;
    // 开启定位
    [self.manager startUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate代理方法
/*
 注意:

 1.定位频率和定位精度并不应当越精确越好,需要视实际情况而定,因为越精确越耗性能,也就越费电。

 2.定位成功后会根据设置情况频繁调用-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations方法,这个方法返回一组地理位置对象数组,每个元素一个CLLocation代表地理位置信息(包含经度、纬度、海报、行走速度等信息),之所以返回数组是因为有些时候一个位置点可能包含多个位置。

 3.使用完定位服务后如果不需要实时监控应该立即关闭定位服务以节省资源。

 4.除了提供定位功能,CLLocationManager还可以调用startMonitoringForRegion:方法对指定区域进行监控。


 */
// 定位成功之后开始更新位置信息,只要移动设置的最小距离之后也开始走这个方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    // 获取最后一次的位置(经纬度)
  //CLLocation用来表示某个位置的地理信息,比如经纬度、海拔等等

  // 经纬度
  @property(readonly, nonatomic) CLLocationCoordinate2D coordinate;

  // 海拔
  @property(readonly, nonatomic) CLLocationDistance altitude;

  // 路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表正北方向)
  @property(readonly, nonatomic) CLLocationDirection course;
  
  // 行走速度(单位是m/s)
  @property(readonly, nonatomic) CLLocationSpeed speed;

  // 此方法可以计算2个位置(CLLocation)之间的距离
  - (CLLocationDistance)distanceFromLocation:(const CLLocation *)location

    CLLocation *location = locations.lastObject;
    CLLocationCoordinate2D coordinate = location.coordinate;
    NSLog(@"**********latitude = %f**********, **********longitude = %f**********, **********altitude = %f**********, 航向 = %f, 行走速度 = %f", coordinate.latitude, coordinate.longitude, locations.lastObject.altitude, locations.lastObject.course, locations.lastObject.speed);
    // 为了节省电源,如果不适用定位,需要把定位关掉
    [self.manager stopUpdatingLocation];
}
// 定位失败
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"定位失败");
}

  3、地理编码与地理反编码

  • 使用CLGeocoder可以完成”地理编码”和”反地理编码”。
  • 地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)。
  • 反地理编码:根据给定的经纬度,获得具体的位置信息。
  • CLPlacemark的字面意思是地标,封装详细的地址位置信息。

// 地理位置
@property (nonatomic, readonly) CLLocation *location;

// 区域
@property (nonatomic, readonly) CLRegion *region;

// 详细的地址信息
@property (nonatomic, readonly) NSDictionary *addressDictionary;

// 地址名称
@property (nonatomic, readonly) NSString *name;

// 地点名称 
@property (nonatomic, readonly) NSString *locality;
  • 代码示例:

@interface ViewController ()<CLLocationManagerDelegate>
/// CLGeocoder能进行地理位置的编码和反编码
@property (nonatomic, strong) CLGeocoder *geocoder;

@end
- (void)viewDidLoad {
    // 初始化对象
    self.geocoder = [[CLGeocoder alloc] init];
    // 根据地名获取经纬度
    [self getCoordinateByAddress:@"北京"];
    // 根据经纬度反编码取出地名
    [self getAddressByLongitude:116 Latitude:40];
    // 计算两个位置之间的距离
    [self distance];
}

#pragma mark - 根据地名获取相关的信息
- (void)getCoordinateByAddress:(NSString *)address
{
    // 编码方法
    [self.geocoder geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        NSLog(@"count1111 = %ld", placemarks.count);
        for (int i = 0; i < placemarks.count; i++) {
            // 根据返回的地标,取出第一个位置(地标的位置很多)
            CLPlacemark *mark = placemarks[i];
            // 根据地标得到location
            CLLocation *location = mark.location;
            // 根据location获取区域
            CLRegion *region = mark.region;
            // 获取字典信息
            NSDictionary *addressDic = mark.addressDictionary;

            NSLog(@"地标 = %@,区域 = %@,字典 = %@",location,region,addressDic);
        }

    }];
}

#pragma mark - 根据经纬度反编码出地名
- (void)getAddressByLongitude:(CLLocationDegrees)longitude Latitude:(CLLocationDegrees)latitude
{
    // 反编码
    // 创建CLLocation
    CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
    [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        NSDictionary *dict = placemarks.lastObject.addressDictionary;
        NSLog(@"count=  %ld", placemarks.count);

        NSLog(@"dic = %@", dict);
    }];
}
#pragma mark - 计算两个位置之间的距离
- (void)distance
{
    /*
     北京的经纬度是 北纬40,东经116
     大连的:北纬39,东经121
     */
    // 创建位置1
    CLLocation *locationBeiJing = [[CLLocation alloc] initWithLatitude:40 longitude:116];
    // 创建位置2
    CLLocation *locationDaLian = [[CLLocation alloc] initWithLatitude:39 longitude:121];
    CLLocationDistance distance = [locationBeiJing distanceFromLocation:locationDaLian];
    NSLog(@"北京到大连的距离:%f", distance);



}

三、地图显示(MapKit框架)

  1、MapKit框架的使用

  • 导入主头文件:#import <MapKit/MapKit.h>
  • MapKit框架使用须知:
  • MapKit框架中所有数据类型的前缀都是MK。
  • MapKit有一个比较重要的UI控件:MKMapView,专门用于地图显示。

  2、跟踪显示用户的位置

  • 设置MKMapView的userTrackingMode属性可以跟踪显示用户的当前位置  。 
  • MKUserTrackingModeNone :不跟踪用户的位置。
  • MKUserTrackingModeFollow :跟踪并在地图上显示用户的当前位置。
  • MKUserTrackingModeFollowWithHeading
    :跟踪并在地图上显示用户的当前位置,地图会跟随用户的前进方向进行旋转。
  • 下图是跟踪效果:
  • 操作系统 3
  • 蓝色发光圆点就是用户的当前位置。
  • 蓝色发光圆点,专业术语叫做“大头针”。

  3、地图的类型

  • 可以通过设置MKMapView的mapType设置地图类型(mapViewType是枚举类型)
  • MKMapTypeStandard // 普通地图
  • 操作系统 4
  • MKMapTypeSatellite // 卫星云图
  • 操作系统 5
  • MKMapTypeHybrid // 普通地图覆盖于卫星云图之上
  • 操作系统 6
  • MKMapTypeSatelliteFlyover NS_ENUM_AVAILABLE(10_11, 9_0) //
    地形和建筑物的三维模型
  • MKMapTypeHybridFlyover NS_ENUM_AVAILABLE(10_11, 9_0) //
    显示道路和附加元素的Flyover

  4、MKMapView的代理

  • MKMapView可以设置一个代理对象,用来监听地图的相关行为,常见的代理方法有:

// 一个位置更改默认只会调用一次,不断监测用户的当前位置时每次都会调用这个方法,把用户的最新位置(userLocation参数)传进来.

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;

// 地图的显示区域即将发生改变的时候调用

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;

// 地图的显示区域已经发生改变的时候调用

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;

  5、MKUserLocation

  • MKUserLocation其实是个大头针模型,包括以下属性:

// 显示在大头针上的标题
@property (nonatomic, copy) NSString *title;

// 显示在大头针上的子标题
@property (nonatomic, copy) NSString *subtitle;

// 地理位置信息(大头针钉在什么地方)
@property (readonly, nonatomic) CLLocation *location;

  6、设置地图的显示

  • 通过MKMapView的下列方法,可以设置地图显示的位置和区域

// 设置地图的中心点位置
@property (nonatomic) CLLocationCoordinate2D centerCoordinate;

-(void)setCenterCoordinate: (CLLocationCoordinate2D)coordinate animated:(BOOL)animated;

@property (nonatomic) MKCoordinateRegion region;
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;

//MKCoordinateRegion是一个用来表示区域的结构体,定义如下
typedef struct {
    CLLocationCoordinate2D center; // 区域的中心点位置
 MKCoordinateSpan span; // 区域的跨度
} MKCoordinateRegion;

MKCoordinateSpan的定义
typedef struct {
    CLLocationDegrees latitudeDelta; // 纬度跨度
    CLLocationDegrees longitudeDelta; // 经度跨度
} MKCoordinateSpan;

  7、大头针

  • 大头针:在iOS开发中经常会标记某个位置,需要使用地图标注,也就是大家俗称的“大头针”。只要一个NSObject类实现MKAnnotation协议就可以作为一个大头针,通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性,然后在程序中创建大头针对象并调用addAnnotation:方法添加大头针即可(之所以iOS没有定义一个基类实现这个协议供开发者使用,多数原因应该是MKAnnotation是一个模型对象,对于多数应用模型会稍有不同,例如后面的内容中会给大头针模型对象添加其他属性)。

  8、代码示例:

#import "ViewController.h"
//引入框架
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "MyAnnotation.h"
@interface ViewController ()<MKMapViewDelegate>

/// 定位管理器
@property (nonatomic, strong) CLLocationManager *manager;
/// 显示地图的视图
@property (nonatomic, strong) MKMapView *mapView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建视图
    [self createMapView];
}
#pragma mark - 创建视图
- (void)createMapView
{
    // 创建地图并添加到当前视图上
    self.mapView = [[MKMapView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.view addSubview:self.mapView];
    // 设置代理
    self.mapView.delegate = self;

    // 定位
    self.manager = [[CLLocationManager alloc] init];
    if (![CLLocationManager locationServicesEnabled]) {
        NSLog(@"当前设备定位不可用");
    }
    if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
        [self.manager requestWhenInUseAuthorization];
    }
    // 设置地图的定位追踪
    self.mapView.userTrackingMode = MKUserTrackingModeFollow;
    // 设置地图的显示类型
    self.mapView.mapType = MKMapTypeStandard;
    // 添加大头针
    [self addAnnotation];
}

#pragma mark - 添加大头针
- (void)addAnnotation
{
    // 设置位置
    CLLocationCoordinate2D loaction1 = CLLocationCoordinate2DMake(40, 116);

    MyAnnotation *annotation = [[MyAnnotation alloc] init];
    annotation.coordinate = loaction1;
    annotation.title = @"北京";
    annotation.subtitle = @"北京市";

    // 大连
    CLLocationCoordinate2D location2 = CLLocationCoordinate2DMake(39, 121);
    MyAnnotation *annotation2 = [[MyAnnotation alloc] init];
    annotation2.coordinate = location2;
    annotation2.title = @"大连";
    annotation2.subtitle = @"大连市";


    // 上海
    CLLocationCoordinate2D location3 = CLLocationCoordinate2DMake(31.23, 121.47);
    MyAnnotation *annotation3 = [[MyAnnotation alloc] init];
    annotation3.coordinate = location3;
    annotation3.title = @"上海";
    annotation3.subtitle = @"上海市";
    [self.mapView addAnnotations:@[annotation, annotation2, annotation3]];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

  大头针模型:

@interface MyAnnotation : NSObject<MKAnnotation>
// 重写协议中的三个属性

// 通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;


@end

四、自定义大头针

  • 很多情况下,需要自定义大头针的显示样式,比如显示一张图片:
  • 操作系统 7
  • 地图上的大头针控件是MKAnnotationView:

//MKAnnotationView的属性

// 大头针模型
@property (nonatomic, strong) id <MKAnnotation> annotation;

// 显示的图片
@property (nonatomic, strong) UIImage *image;

// 是否显示标注
@property (nonatomic) BOOL canShowCallout;

// 标注的偏移量
@property (nonatomic) CGPoint calloutOffset;

// 是否显示标注
@property (nonatomic) BOOL canShowCallout;

// 标注的偏移量
@property (nonatomic) CGPoint calloutOffset;

// MKPinAnnotationView是MKAnnotationView的子类

// MKPinAnnotationView比MKAnnotationView多了2个属性

// 大头针颜色
@property (nonatomic) MKPinAnnotationColor pinColor;

// 大头针第一次显示时是否从天而降
@property (nonatomic) BOOL animatesDrop;
  • 设置MKMapView的代理实现下面的代理方法,返回大头针控件

#pragma mark - 实现自定义大头针视图的代理方法
// 显示大头针的时候才会调用的
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    // 判断是否是当前自定义的大头针类
    if ([annotation isKindOfClass:[MyAnnotation class]]) {
        // 先定义一个重用标识
        static NSString *identifier = @"AnnotationOne";
        MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (!annotationView) {
            annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            // 允许用户交互
            annotationView.canShowCallout = YES;
            // 设置图片
            annotationView.image = [UIImage imageNamed:@"eye"];
            // 设置详情和大头针偏移量
            annotationView.calloutOffset = CGPointMake(0, 3);
            // 设置详情的左视图
            annotationView.leftCalloutAccessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Y0N[2Z@P[]SRYYA@I0CNNTO.jpg"]];
        }
        return annotationView;
    } else {
        return nil;
    }
}

一、地图的简介
在移动互联网时代,移动app能解决用户的很多生活琐事,比如:
导航:去任意陌生的地方。…

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新特性,混合地图,支持城市观光

四. 自定义大头针

MKAnnotation是一个大头针模型.包括以下属性:
1.title大头针的标题
2.subTitle大头针的子标题
3.coordinate;坐标位置信息.
注意:一般的使用大头针模型,都是用的是其子类,不是其父类.
使用的时候,初始化大头针对象,并设置响应的属性值,重点是:设置大头针的coordinate的属性值,这个为大头针的位置信息,使用CLLocationCoordinate2DMake(x,y);函数确定,函数内的参数为维度,经度.然后通过mapView对象的
addAnnotation方法添加到地图上进行显示.或者使用addAnnotations:(NSArray
*)annotations;添加多个大头针.移除大头针的方法也有类似removeAnnotation移除一个大头针,removeAnnotations:(NSArrray
*)annotations移除多个大头针.

自定义大头针:

一般的,相关地图的应用程序所使用到的大头针都是通过自定义而生成的特殊样式.通过MKMapView的代理方法来实现.
示例源码:

// 当MapView调用addAnnotation方法的时候,响应此方法,默认返回值为nil为默认大头针样式,通过参数annotation的不同,返回不同的MKAnnotationView的类型.(MKPINAnnotationView:为MKAnnotationView的子类,比其父类多两个属性,pinTintColor大头针的颜色,animatesDrop:从天而降的动画效果)
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
    if ([annotation isKindOfClass:[MyAnnonation class]]) {
        MKPinAnnotationView *pinAnnotationView = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@"reuse"];
        pinAnnotationView.canShowCallout = YES;
        pinAnnotationView.pinTintColor = [UIColor cyanColor];
        pinAnnotationView.animatesDrop = YES;
        return pinAnnotationView;
    } else {
        return nil;
    }
}

系统自带地图导航

  • 要使用地图导航功能在自带地图应用中相当简单,只要设置参数配置导航模式即可。
  • 效果展示

操作系统 8

926181116920.gif

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@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

二. 地图定位(CoreLocation框架,地理编码与反地理编码)

地图定位使用步骤.

    // 定位控制器
    CLLocationManager *manager = [[CLLocationManager alloc] init];
    // 每隔多少具体定位一次
    manager.distanceFilter = 100;
    // 定位精度
    manager.desiredAccuracy = kCLLocationAccuracyBest;
    // 指定代理
    manager.delegate = self;
    // 开启定位服务
    [manager startUpdatingLocation];

为了在保险起见,开发者不确定用户是否在设置中将其定位功能进行关闭,所以将使用CLLocationManager的一个类方法进行判断用户是否开启了定位服务

+(BOOL)locationServicesEnable;// 返回值为BOOL类型

开发者也可以根据Info.plist添加键值对,用来说明定位服务具体的用处.

// 需要在Info.plist文件中添加相应的键值对来配合该方法的使用
[manager requestAlwaysAuthorization];

Info.plist文件中的键:NSLocationAlwaysUsageDescription
CLLocationManager的代理方法:
获取定位信息的代理方法:

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations;

一般的获得的定位信息在存储在locations数组中,并且,数组中的元素的类型为CLLocation类型
CLLocation类中的coordinate属性为经纬度的结构体(CLLocationCoordinate2D的结构体),信息longitude为经度,latitude维度.
获取定位失败:

-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error 

地理编码与反编码:
使用CLGeocoder类的实例化对象可以将具体地址转化为经纬度信息.地理编码.

// CLGeocoder的实例方法,地理编码(编码完成的信息存放在Block参数中的NSArray中,为CLPlacemark类型,此类型中有很多关于具体地理位置信息的属性)
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

使用CLGeocoder类的实例化对象可以将经纬度转化为地区的具体地址,
地理反编码.

// CLGeocoder的实例方法,反地理编码(String为所输入的城市获具体地址的名称),通过反地理编码,会将转换过来的经纬度信息存放在Block参数的一个数组中(placemarks)为其中的第一个元素,
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;

CLPlacemark 地标:
中有具体的属性,显示当前位置的具体信息.

MKMapView 常用方法
  • 初始化方法

- (instancetype)initWithFrame:(CGRect)frame;
  • 实例化方法

// 1、添加大头针,对应的有添加大头针数组
 - (void)addAnnotation:(id <MKAnnotation>)annotation;

// 2、删除大头针,对应的有删除大头针数组 
- (void)removeAnnotation:(id <MKAnnotation>)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 <MKAnnotation>)annotation animated:(BOOL)animated; 

// 9、取消选中指定的大头针
- (void)deselectAnnotation:(nullable id <MKAnnotation>)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 <MKAnnotation>)annotation; 

// 4、点击选中某个大头针时触发 
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view; 

// 5、取消选中大头针时触发 
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view; 

// 6、渲染地图覆盖物时触发 
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay

一. 地图简介

在移动互联网的时代,移动App能进解决用户的很多生活琐事,比如导航,地图等,这些应用均使用到了地图与定位.在iOS开发过程中,想要将应用加入这两大功能则必须在下面两个框架中进行开发.Map
Kit用于地图的显示与Core Location用于地理定位.

注: iOS中国大陆地区的终端用户的地图数据使用的是高德地图的数据.

定位

三. 地图显示(MapKit框架)

需要导入头文件#import<MapKit/MapKit.h>
MKMapView用来显示地图.
MKMapView的实例化对象中有一个属性userTrackingMode用来设置跟踪用户类型.mapType则是设置地图类型.
获取定位,需要设置地图的代理,需要实现代理方法.

定位功能
  • 定位主要用于获取用户当前地理位置信息。
  • 常用的定位技术:
    GPS定位(全球定位系统:最精准的定位方式);
    Skyhook Wi-Fi定位(Wi-Fi路由器);
    因特网提供商定位技术(提供商中心站);
    多种定位方法混合使用;
  • Core
    Location是iOS中实现定位功能的框架,提供了大量接口对定位功能进行配置及获取地理位置数据。
  • 使用Core Location需要导入框架:CoreLocation.framework。
  • CoreLocation提供了CLLocationManager类来对定位功能进行管理,包括定位功能的配置、开始、停止定位等。
  • CoreLocation也提供了CLLocationManagerDelegate委托协议来处理CLLocationManager在定位过程中触发的事件。
CLLocationManager 定位代码示例
#import "ViewController.h"
// 定位框架
#import <CoreLocation/CoreLocation.h>

#define NSLOG(OBJECT)  NSLog(@"%@", OBJECT)

@interfaceViewController () <CLLocationManagerDelegate>
{
    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<CLLocation *> *)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:方法对指定区域进行监控。

地图

  • iOS从6.0开始地图数据不再由谷歌驱动,而是改用自家地图,当然在国内它的数据是由高德地图提供的。这样一来,如果在iOS6.0之前进行地图开发的话使用方法会有所不同,基于目前的情况其实使用iOS6.0之前版本的系统基本已经寥寥无几了,所有在接下来的内容中不会再针对iOS5及之前版本的地图开发进行介绍。

  • 在iOS中进行地图开发主要有两种方式,一种是直接利用MapKit框架进行地图开发,利用这种方式可以对地图进行精准的控制;另一种方式是直接调用苹果官方自带的地图应用,主要用于一些简单的地图应用(例如:进行导航覆盖物填充等),无法进行精确的控制。当然,本节重点内容还是前者,后面的内容也会稍加提示。

  • Map Kit框架中 MKMapView类 提供了系统自带的地图界面效果,配合Core
    Location的使用可以展示多样化的信息。

  • 使用Map Kit需要导入框架:MapKit.framework

CLLocationManager 工作流程

操作系统 9

926110847675.png

  • 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;

导航

  • 在自定义标注视图上添加导航按钮(这里需说明:如果添加在标注视图上的控件继承于UIControl,无需添加事件),在点击导航按钮时会自动触发calloutAccessoryControlTapped:方法,如下将实现地图导航路线绘制关键代码。

  • 效果展示

操作系统 10

926181326921.gif

  • 代码示例

- (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<MKOverlay>)overlay {
    // 初始化路线渲染器
    MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
    // 设置路线宽度
    renderer.lineWidth = 2.0;
    // 设置路线颜色
    renderer.strokeColor = [UIColor redColor];
    return renderer;
}
大头针
  • 在iOS开发中经常会标记某个位置,需要使用地图标注,也就是大家俗称的“大头针”。只要一个NSObject类实现MKAnnotation协议就可以作为一个大头针,通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性,然后在程序中创建大头针对象并调用addAnnotation:方法添加大头针即可(之所以iOS没有定义一个基类实现这个协议供开发者使用,多数原因应该是MKAnnotation是一个模型对象,对于多数应用模型会稍有不同,例如后面的内容中会给大头针模型对象添加其他属性)。下面代码示例中,实现通过长按手势,添加大头针逻辑。

操作系统 11

926141006362.png

- (void)respondsToGesture:(UILongPressGestureRecognizer *)gesture { // 当长按手势开始时,添加一个标注数据源if (gesture.state == UIGestureRecognizerStateBegan) { // 获取用户长按手势在地图上的点CGPoint point = [gesture locationInView:self.mapView]; // 将地图上的点转化为经纬度 CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView]; // 创建一个标注数据源,这里使用系统标注数据源:MKPointAnnotation,如果要自定义,必须遵守<MKAnnotation>协议 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<MKAnnotation>)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;
}
用户位置跟踪
  • 在很多带有地图的应用中默认打开地图都会显示用户当前位置,同时将当前位置标记出来放到屏幕中点方便用户对周围情况进行查看。如果在iOS6或者iOS7中实现这个功能只需要添加地图控件、设置用户跟踪模式、在didUpdateUserLocation:代理方法中设置地图中心区域及显示范围。但是在iOS8中用法稍有不同:

      由于在地图中进行用户位置跟踪需要使用定位功能,而定位功能在iOS8中设计发生了变化,因此必须按照前面定位章节中提到的内容进行配置和请求。
    
      iOS8中不需要进行中心点的指定,默认会将当前位置设置中心点并自动设置显示区域范围。
    
  • 了解以上两点,要进行用户位置跟踪其实就相当简单了,值得一提的是didUpdateUserLocation:这个代理方法。这个方法只有在定位到当前位置之后就会调用,以后每当用户位置发生改变就会触发,调用频率相当频繁。

地理编码

除了提供位置跟踪功能之外,在定位服务中还包含CLGeocoder 类用于处理地理编码和逆地理编码(又叫反地理编码)功能。

地理编码:根据给定的位置(通常是地名)确定地理坐标(经、纬度)。

逆地理编码:可以根据地理坐标(经、纬度)确定位置信息(街道、门牌等)。
  • 地理编码方法

// 1、地理编码 
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;

// 2、逆地理编码
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
  • 地理编码示例

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>

@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<CLPlacemark *> * _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<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@", error.localizedDescription);
        }else {
            CLPlacemark *placemark=[placemarks firstObject];
            NSLog(@"详细信息:%@",placemark.addressDictionary);
        }
    }];
}

@end
  • 注意:地理编码和逆地理编码不可同时执行;
特别说明
  • 由于定位和地图框架中用到了诸多类,有些初学者容易混淆,下面简单对比一下。

    CLLocation:用于表示位置信息,包含地理坐标、海拔等信息,包含在CoreLoaction框架中。

    MKUserLocation:一个特殊的大头针,表示用户当前位置。

    CLPlacemark:定位框架中地标类,封装了详细的地理信息。

    MKPlacemark:类似于CLPlacemark,只是它在MapKit框架中,可以根据CLPlacemark创建MKPlacemark。