定位是一个很经常使用的功能,如一些地图软件打开以后 若是用户容许软件定位的话,那么打开软件后就会自动锁定到当前位置,若是用户手机移动那么当前位置也会跟随着变化。要实现这个功能须要使用Core Loaction中CLLocationManager类。git
经常使用类:以CL前缀开头web
CLLocation:(结构体类型)经纬度数组
CLLocationManager: 定位管理类,位置管理器,全局惟一存在,作定位用。 微信
CLLocationManagerDelegate: 监听用户是否愿意定位(iOS8后要问),监听用户的位置(经纬度)框架
经常使用类:以MK前缀开头优化
MKMapView: 显示地图视图编码
MKMapViewDelegate: 地图视图的协议(定位;地图视图移动;定位用户的位置)atom
从iOS 6开始,苹果在保护用户隐私方面作了很大的增强,如下操做都必须通过用户批准受权spa
(1)要想得到用户的位置3d
(2)想访问用户的通信录、日历、相机、相册等
当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户受权
注意:一旦用户选择了“Don’t Allow”,意味着你的应用之后就没法使用定位功能,且当用户第一次选择了以后,之后就不再会提醒进行设置。
所以在程序中应该进行判断,若是发现本身的定位服务没有打开,那么应该提醒用户打开定位服务功能。
CLLocationManager有个类方法能够判断当前应用的定位功能是否可用+ (BOOL)locationServicesEnabled;
经常使用的方法:截图告诉用户,应该怎么打开受权
配置征求用户是否赞成受权的弹出框:(在Info.plist里配置,添加相应的key 才能弹出受权框)
自定义一个位置模拟位置,写哪儿模拟器就认为你在哪儿(定位就定在这儿)
#pragma mark - CLLocationManagerDelegate 实现协议中的方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
#import "ViewController.h" #import <CoreLocation/CoreLocation.h> // 导入头文件 @interface ViewController () <CLLocationManagerDelegate> // 声明一个属性帮助咱们来定位,干什么都要问他 @property (nonatomic, strong) CLLocationManager *manager; @end @implementation ViewController #pragma mark ---懒加载 -(CLLocationManager *)manager { if (_manager==nil) { // 1.建立位置管理器(定位用户的位置) _manager=[[CLLocationManager alloc]init]; // 2.设置代理(设置谁来监听用户的位置) _manager.delegate=self; } return _manager; } - (void)viewDidLoad { [super viewDidLoad]; // 请求用户受权 // iOS8以后才开始征求用户赞成,iOS8以前不用征求赞成直接定位 if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { // 注意:iOS8以后要配置info.plist文件:添加key(NSLocationAlwaysUsageDescription / NSLocationWhenInUseUsageDescription),才能弹出受权框
// 两种受权:始终(永久)受权、使用应用期间受权。
// 注意:若是同时写了下面两种受权,程序打开的时候会出现两次受权提示。大多数状况下,咱们根据程序的需求写一种受权方式就能够了。 //1) 永久受权。不管当前程序在前台或后台都受权/都定位 (key:NSLocationAlwaysUsageDescription) //[self.manager requestAlwaysAuthorization]; //请求老是受权 //2) 当用户正在使用的时候受权。只有程序在前台运行的时候才会受权(征求用户是否愿意只在前台定位) (key:NSLocationWhenInUseUsageDescription) [self.manager requestWhenInUseAuthorization]; //请求在使用时的受权(在前台),大多数APP使用的是这种受权 } else { // 调用开始定位方法。直接定位(不须要征求用户赞成) 定位里面干什么都要问manager [self.manager startUpdatingLocation];//开始定位 } } #pragma mark - CLLocationManagerDelegate 实现协议中的方法 //1.查看用户是否赞成(这个方法监听用户有没有点容许/不容许),用户赞成了就调用第二个方法 /** * @param status 用户受权的状态 (用户是否赞成) * 经常使用的两个状态: * 1) kCLAuthorizationStatusDenied:用户不一样意定位 * 用户不容许自动定位时,能够手动选择城市定位(如:墨迹天气,用户手动选择一个城市,把城市的天气推送给你) * 2) kCLAuthorizationStatusAuthorizedWhenInUse:用户容许在使用期间(前台)定位 */ -(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { //判断两种经常使用的受权状态 switch (status) { case kCLAuthorizationStatusAuthorizedWhenInUse: //用户容许在使用期间(前台)定位
// 实现持续定位,一般要作一些优化(目的是节省电量和流量)。
// 1. 设置距离筛选器,当用户位置发生必定改变以后再调用代理方法2(避免调用太频繁)。
// 当用户位置发生超过10米的变化,再从新开始定位(即调用代理方法2)
self.manger.distanceFilter = 10; // 2. 设置定位的精准度(常量值:kXXXBest最好的、十米范围内、百米范围内、公里范围内、三公里范围内) // 咱们能够下降定位的精准度,实际上下降了与卫星之间的计算,以此节省电量和流量。精确度越高越费电量/流量,通常选十米/百米范围内为宜。 self.manager.desiredAccuracy = kCLLocationAccuracyBest;//定位的精确度 [self.manager startUpdatingLocation]; //开始定位操做 break; case kCLAuthorizationStatusDenied: //用户不容许定位(第二种方案) NSLog(@"用户不容许定位!"); break; default: break; } } //2.已经定位到用户的位置会调用这个方法 /** * 当完成位置更新的时候调用。当定位到用户的位置时,就会调用(调用的频率比较频繁)
* 当调用了startUpdatingLocation方法后,就开始不断地定位用户的位置,中途会频繁地调用代理的下面方法 * @param locations 用户的位置(数组类型,最后一项是用户最新的位置;数组里至少有一项) */ -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *location=[locations lastObject];//获取用户最新的位置 NSLog(@"纬度:%f, 经度:%f", location.coordinate.latitude, location.coordinate.longitude); //打印获取用户位置的经纬度 // 中止用户定位/中止更新位置(不中止,就会一直不停的定位,即持续定位;中止后该代理方法就不会再频繁调用,即一次定位) [self.manager stopUpdatingLocation]; // Xcode7如下只定位一次(打印经纬度一次),Xcode7以上会定位三次(打印经纬度三次) } @end
注意:若是发现本身的定位服务没有打开,那么应该提醒用户打开定位服务功能。定位服务是比较耗电的,若是是作定位服务(不必实时更新的话),那么定位了用户位置后,应该中止更新位置。
// 比较两个位置之间的距离(如,北京与西安的距离) CLLocation *location1 = [[CLLocation alloc]initWithLatitude:40 longitude:116]; CLLocation *location2 = [[CLLocation alloc]initWithLatitude:34.27 longitude:108.93]; // 比较直线距离 CLLocationDistance distance = [location1 distanceFromLocation:location2]; NSLog(@"北京与西安的直线距离为:%f公里", distance / 1000);
Geography(地理) + code(编码),CLGeocoder:地理编码器,其中Geo是地理英文单词Geography的简写。
使用 CLGeocoder 能够完成“地理编码”和“反地理编码”
@interface CLPlacemark : NSObject //地标类 @property (nonatomic, readonly, copy) CLLocation *location; //地理位置,能够获取经纬度 @property (nonatomic, readonly, copy) NSDictionary *addressDictionary; //详细的地址信息 @property (nonatomic, readonly) CLRegion *region; //区域 @property (nonatomic, readonly) NSString *name; //地址名称 @property (nonatomic, readonly) NSString *locality; //城市 @end
// 1. 建立一个CLGeocoder对象 CLGeocoder *geocoder = [[CLGeocoder alloc] init]; // 2. 开始地理编码 /** * 说明:调用下面的方法开始编码,无论编码是成功仍是失败都会调用block中的方法 * 给一个地名,返回一个block回调参数 * @param placemarks 地标数组,主要的是CLLocation / 城市属性 */ [geocoder geocodeAddressString:@"下沙" completionHandler:^(NSArray *placemarks, NSError *error) { //1)若是有错误信息,或者是数组中获取的地名元素数量为0,那么说明没有找到 if (placemarks.count == 0 || error) { NSLog(@"你输入的地址没找到,可能在火星上"); } else { //2)编码成功,找到了具体的位置信息 /** * 遍历地表数组: * 这里数组中有一个/多个相关的位置信息对象(给一个名称,可能对应多个位置信息) */ for (CLPlacemark *placemark in placemarks) { // 打印查看找到的全部的位置信息 NSLog(@"详细地址名称:%@", placemark.name); NSLog(@"经纬度坐标:%.4f, %.4f" , placemark.location.coordinate.longitude, placemark.location.coordinate.latitude); } // 取得第一个地标,地标中存储了详细的地址信息,注意:一个地名可能搜索出多个地址 CLPlacemark *placemark = [placemarks firstObject]; //1>详细地址名称 NSLog(@"详细地址名称:%@", placemark.name); //2>经纬度 NSLog(@"经纬度坐标:%.4f, %.4f" , placemark.location.coordinate.longitude, placemark.location.coordinate.latitude); } }];
// 1. 设置经纬度 CLLocation *location = [[CLLocation alloc]initWithLatitude:40 longitude:116]; // 2.开始反地理编码 [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { // 1)若是有错误信息,或者是数组中获取的地名元素数量为0,那么说明没有找到 if (placemarks.count == 0 || error) { NSLog(@"你输入的地址没找到,可能在火星上"); } else { //2)编码成功 //这里不用for循环遍历,由于数组中只有惟一的一个对象(经纬度必定,地名也必定),直接取出便可 CLPlacemark *placemark = [placemarks firstObject]; self.reverseDetailAddressLabel.text = placemark.name; NSLog(@"详细地址名称:%@", placemark.addressDictionary[@"FormattedAddressLines"]); } }];
MapKit中有一个比较重要的UI控件:MKMapView,专门用于地图显示。
#import "ViewController.h" #import <MapKit/MapKit.h> @interface ViewController () <MKMapViewDelegate> @property (weak, nonatomic) IBOutlet MKMapView *mapView; @property (nonatomic, strong) CLLocationManager *manager; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 初始化manager self.manager = [[CLLocationManager alloc]init]; // 请求受权:征求用户赞成(假定赞成,在targets-->Info/Info.plist中添加key) if ([self.manager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { [self.manager requestWhenInUseAuthorization]; } //设置地图视图的代理 self.mapView.delegate = self; // 设置地图的属性 self.mapView.rotateEnabled = NO;//设置不容许旋转 self.mapView.mapType = MKMapTypeHybrid;//地图的显示类型(混合类型,默认是Standard类型) // 设定地图属性(开始定位/已经把用户的位置显示在地图上) // 用户的跟踪模式,地图视图的跟踪模式是跟随着用户的位置而变化,当前地图必定会显示用户的位置 self.mapView.userTrackingMode=MKUserTrackingModeFollow; } #pragma mark - MKMapViewDelegate 协议 // 完成用户位置更新的时候调用,已经定位到用户的位置调用这个方法 -(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { // 已经定位到用户的位置 NSLog(@"location:%f, %f",userLocation.location.coordinate.latitude, userLocation.location.coordinate.longitude);
userLocation.title = @"用户位置-标题";
userLocation.subtitle = @"用户位置-子标题";
// 这里能够经过反地理编码把经纬度转成地名,用户点击时就能够显示用户位置的提示。 } @end