iOS开发 - CoreLocation地理定位

来源:互联网 发布:淘师湾算法及其描述 编辑:程序博客网 时间:2024/05/17 09:32

简介

在移动互联网时代,移动app能解决用户的很多生活琐事,比如
导航:去任意陌生的地方
周边:找餐馆、找酒店、找银行、找电影院

在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这2大功能,必须基于2个框架进行开发
Map Kit :用于地图展示
Core Location :用于地理定位

2个热门专业术语
LBS :Location Based Service
SoLoMo :Social Local Mobile(索罗门)

CoreLocation框架的使用

CoreLocation框架使用前提
导入框架
这里写图片描述

导入主头文件

#import <CoreLocation/CoreLocation.h>

CoreLocation框架使用须知
CoreLocation框架中所有数据类型的前缀都是CL
CoreLocation中使用CLLocationManager对象来做用户定位

CLLocationManager

CLLocationManager的常用操作//开始用户定位- (void)startUpdatingLocation;//停止用户定位- (void) stopUpdatingLocation;//当调用了startUpdatingLocation方法后,就开始不断地定位用户的位置,中途会频繁地调用代理的下面方法- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;locations参数里面装着CLLocation对象@property(assign, nonatomic) CLLocationDistance distanceFilter;//每隔多少米定位一次@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;//定位精确度(越精确就越耗电)

CLLocation

CLLocation用来表示某个位置的地理信息,比如经纬度、海拔等等

@property(readonly, nonatomic) CLLocationCoordinate2D coordinate;//经纬度@property(readonly, nonatomic) CLLocationDistance altitude;//海拔@property(readonly, nonatomic) CLLocationDirection course;//路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表真北方向)@property(readonly, nonatomic) CLLocationSpeed speed;//行走速度(单位是m/s)用- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location方法可以计算2个位置之间的距离

CLLocationCoordinate2D

CLLocationCoordinate2D是一个用来表示经纬度的结构体,定义如下typedef struct {        CLLocationDegrees latitude; // 纬度        CLLocationDegrees longitude; // 经度} CLLocationCoordinate2D;//一般用CLLocationCoordinate2DMake

用户隐私的保护

从iOS 6开始,苹果在保护用户隐私方面做了很大的加强,以下操作都必须经过用户批准授权
要想获得用户的位置
想访问用户的通讯录、日历、相机、相册等等

当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户授权

开发者可以在Info.plist中设置NSLocationUsageDescription说明定位的目的(Privacy - Location Usage Description)

一旦用户选择了“Don’t Allow”,意味着你的应用以后就无法使用定位功能
为了严谨起见,最好在使用定位功能之前判断当前应用的定位功能是否可用
//CLLocationManager有个类方法可以判断当前应用的定位功能是否可用
+ (BOOL)locationServicesEnabled;

CLGeocoder

使用CLGeocoder可以完成“地理编码”和“反地理编码”
地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)
反地理编码:根据给定的经纬度,获得具体的位置信息

//地理编码方法- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;//反地理编码方法- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

CLGeocodeCompletionHandler

//当地理\反地理编码完成时,就会调用CLGeocodeCompletionHandlertypedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);这个block传递2个参数error :当编码出错时(比如编码不出具体的信息)有值placemarks :里面装着CLPlacemark对象

标题

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;//城市

关系图

这里写图片描述

CoreLocation实例

@interface ViewController ()<CLLocationManagerDelegate>/** *  定位管理者 */@property (nonatomic ,strong) CLLocationManager *mgr;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // 1.创建CoreLocation管理者    CLLocationManager *mgr = [[CLLocationManager alloc] init];    // 2.成为CoreLocation管理者的代理监听获取到的位置    self.mgr.delegate = self;    // 设置多久获取一次    self.mgr.distanceFilter = 500;    // 设置获取位置的精确度    /*      kCLLocationAccuracyBestForNavigation 最佳导航      kCLLocationAccuracyBest;  最精准      kCLLocationAccuracyNearestTenMeters;  10米      kCLLocationAccuracyHundredMeters;  百米      kCLLocationAccuracyKilometer;  千米      kCLLocationAccuracyThreeKilometers;  3千米     */    self.mgr.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;    // 判断是否是iOS8    if([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0)    {        NSLog(@"是iOS8");        // 主动要求用户对我们的程序授权, 授权状态改变就会通知代理        //        [self.mgr requestAlwaysAuthorization]; // 请求前台和后台定位权限//        [self.mgr requestWhenInUseAuthorization];// 请求前台定位权限    }else    {        NSLog(@"是iOS7");        // 3.开始监听(开始获取位置)        [self.mgr startUpdatingLocation];    }}/** *  授权状态发生改变时调用 * *  @param manager 触发事件的对象 *  @param status  当前授权的状态 */- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{    /*     用户从未选择过权限     kCLAuthorizationStatusNotDetermined     无法使用定位服务,该状态用户无法改变     kCLAuthorizationStatusRestricted     用户拒绝该应用使用定位服务,或是定位服务总开关处于关闭状态     kCLAuthorizationStatusDenied     已经授权(废弃)     kCLAuthorizationStatusAuthorized     用户允许该程序无论何时都可以使用地理信息     kCLAuthorizationStatusAuthorizedAlways     用户同意程序在可见时使用地理位置     kCLAuthorizationStatusAuthorizedWhenInUse     */    if (status == kCLAuthorizationStatusNotDetermined) {        NSLog(@"等待用户授权");    }else if (status == kCLAuthorizationStatusAuthorizedAlways ||              status == kCLAuthorizationStatusAuthorizedWhenInUse)    {        NSLog(@"授权成功");        // 开始定位        [self.mgr startUpdatingLocation];    }else    {        NSLog(@"授权失败");    }}#pragma mark - CLLocationManagerDelegate//- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation/** *  获取到位置信息之后就会调用(调用频率非常高) * *  @param manager   触发事件的对象 *  @param locations 获取到的位置 */- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{    NSLog(@"%s", __func__);    // 如果只需要获取一次, 可以获取到位置之后就停止//    [self.mgr stopUpdatingLocation];}#pragma mark - 懒加载- (CLLocationManager *)mgr{    if (!_mgr) {        _mgr = [[CLLocationManager alloc] init];    }    return _mgr;}@end
    /*     注意: iOS7只要开始定位, 系统就会自动要求用户对你的应用程序授权. 但是从iOS8开始, 想要定位必须先"自己""主动"要求用户授权      在iOS8中不仅仅要主动请求授权, 而且必须再info.plist文件中配置一项属性才能弹出授权窗口     NSLocationWhenInUseDescription,允许在前台获取GPS的描述     NSLocationAlwaysUsageDescription,允许在后台获取GPS的描述    */
@interface ViewController ()<CLLocationManagerDelegate>/** *  定位管理者 */@property (nonatomic ,strong) CLLocationManager *mgr;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // 1.创建CoreLocation管理者//    CLLocationManager *mgr = [[CLLocationManager alloc] init];    // 2.成为CoreLocation管理者的代理监听获取到的位置    self.mgr.delegate = self;    // 判断是否是iOS8    if([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0)    {        NSLog(@"是iOS8");        // 主动要求用户对我们的程序授权, 授权状态改变就会通知代理        [self.mgr requestAlwaysAuthorization]; // 请求前台和后台定位权限    }else    {        NSLog(@"是iOS7");        // 3.开始监听(开始获取位置)        [self.mgr startUpdatingLocation];    }}/** *  授权状态发生改变时调用 * *  @param manager 触发事件的对象 *  @param status  当前授权的状态 */- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{   if (status == kCLAuthorizationStatusNotDetermined) {        NSLog(@"等待用户授权");    }else if (status == kCLAuthorizationStatusAuthorizedAlways ||              status == kCLAuthorizationStatusAuthorizedWhenInUse)    {        NSLog(@"授权成功");        // 开始定位        [self.mgr startUpdatingLocation];    }else    {        NSLog(@"授权失败");    }}#pragma mark - CLLocationManagerDelegate/** *  获取到位置信息之后就会调用(调用频率非常高) * *  @param manager   触发事件的对象 *  @param locations 获取到的位置 */- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{    NSLog(@"%s", __func__);    // 如果只需要获取一次, 可以获取到位置之后就停止//    [self.mgr stopUpdatingLocation];    // 1.获取最后一次的位置    /*     location.coordinate; 坐标, 包含经纬度     location.altitude; 设备海拔高度 单位是米     location.course; 设置前进方向 0表示北 90东 180南 270西     location.horizontalAccuracy; 水平精准度     location.verticalAccuracy; 垂直精准度     location.timestamp; 定位信息返回的时间     location.speed; 设备移动速度 单位是米/秒, 适用于行车速度而不太适用于不行     */    /*     可以设置模拟器模拟速度     bicycle ride 骑车移动     run 跑动     freeway drive 高速公路驾车     */    CLLocation *location = [locations lastObject];    NSLog(@"%f, %f speed = %f", location.coordinate.latitude , location.coordinate.longitude, location.speed);}#pragma mark - 懒加载- (CLLocationManager *)mgr{    if (!_mgr) {        _mgr = [[CLLocationManager alloc] init];    }    return _mgr;}@end

汽车导航实例

#import "ViewController.h"#import <CoreLocation/CoreLocation.h>@interface ViewController ()<CLLocationManagerDelegate>// 创建定位管理者@property (nonatomic, strong) CLLocationManager *mgr;// 上一次的位置@property (nonatomic, strong) CLLocation *previousLocation;// 总路程@property (nonatomic, assign) CLLocationDistance  sumDistance;// 总时间@property (nonatomic, assign) NSTimeInterval  sumTime;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // 1.创建定位管理者//    CLLocationManager *mgr = [[CLLocationManager alloc] init];    // 2.设置代理    self.mgr.delegate = self;    // 3.判断是否是iOS8    if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) {        // 主动请求授权        [self.mgr requestAlwaysAuthorization];    }    // 3.开始定位    [self.mgr startUpdatingLocation];}#pragma mark - CLLocationManagerDelegate- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{//    CLLocation; timestamp 当前获取到为止信息的时间    /*     获取走了多远(这一次的位置 减去上一次的位置)     获取走这段路花了多少时间 (这一次的时间 减去上一次的时间)     获取速度 (走了多远 / 花了多少时间)     获取总共走的路程 (把每次获取到走了多远累加起来)     获取平均速度 (用总路程 / 总时间)     */    // 获取当前的位置    CLLocation *newLocation = [locations lastObject];    if (self.previousLocation != nil) {        // 计算两次的距离(单位时米)        CLLocationDistance distance = [newLocation distanceFromLocation:self.previousLocation];        // 计算两次之间的时间(单位只秒)        NSTimeInterval dTime = [newLocation.timestamp timeIntervalSinceDate:self.previousLocation.timestamp];        // 计算速度(米/秒)        CGFloat speed = distance / dTime;        // 累加时间        self.sumTime += dTime;        // 累加距离        self.sumDistance += distance;        //  计算平均速度        CGFloat avgSpeed = self.sumDistance / self.sumTime;        NSLog(@"距离%f 时间%f 速度%f 平均速度%f 总路程 %f 总时间 %f", distance, dTime, speed, avgSpeed, self.sumDistance, self.sumTime);    }    // 纪录上一次的位置    self.previousLocation = newLocation;}//#pragma mark - 懒加载- (CLLocationManager *)mgr{    if (!_mgr) {        self.mgr = [[CLLocationManager alloc] init];    }    return _mgr;}@end

指南针实例

#import "ViewController.h"#import <CoreLocation/CoreLocation.h>@interface ViewController ()<CLLocationManagerDelegate>/** *  定位管理者 */@property (nonatomic ,strong) CLLocationManager *mgr;// 指南针图片@property (nonatomic, strong) UIImageView *compasspointer;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // 1.添加指南针图片    UIImageView *iv = [[UIImageView alloc] initWithImage: [UIImage imageNamed:@"bg_compasspointer"]];    iv.center = CGPointMake(self.view.center.x, self.view.center.y);    [self.view addSubview:iv];    self.compasspointer = iv;    // 2.成为CoreLocation管理者的代理监听获取到的位置    self.mgr.delegate = self;    // 3.开始获取用户位置    // 注意:获取用户的方向信息是不需要用户授权的    [self.mgr startUpdatingHeading];}#pragma mark - CLLocationManagerDelegate// 当获取到用户方向时就会调用- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{//    NSLog(@"%s", __func__);    /*     magneticHeading 设备与磁北的相对角度     trueHeading 设置与真北的相对角度, 必须和定位一起使用, iOS需要设置的位置来计算真北     真北始终指向地理北极点     *///    NSLog(@"%f", newHeading.magneticHeading);    // 1.将获取到的角度转为弧度 = (角度 * π) / 180;    CGFloat angle = newHeading.magneticHeading * M_PI / 180;    // 2.旋转图片    /*     顺时针 正     逆时针 负数     *///    self.compasspointer.transform = CGAffineTransformIdentity;    self.compasspointer.transform = CGAffineTransformMakeRotation(-angle);}#pragma mark - 懒加载- (CLLocationManager *)mgr{    if (!_mgr) {        _mgr = [[CLLocationManager alloc] init];    }    return _mgr;}@end

区域检查实例

#import "ViewController.h"#import <CoreLocation/CoreLocation.h>@interface ViewController ()<CLLocationManagerDelegate>/** *  定位管理者 */@property (nonatomic ,strong) CLLocationManager *mgr;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // 2.成为CoreLocation管理者的代理监听获取到的位置    self.mgr.delegate = self;    // 注意:如果是iOS8, 想进行区域检测, 必须自己主动请求获取用户隐私的权限    if  ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0 )    {        [self.mgr requestAlwaysAuthorization];    }    // 3.开始检测用户所在的区域    // 3.1创建区域//    CLRegion 有两个子类是专门用于指定区域的//    一个可以指定蓝牙的范围/ 一个是可以指定圆形的范围    // 创建中心点    CLLocationCoordinate2D center = CLLocationCoordinate2DMake(40.058501, 116.304171);    // c创建圆形区域, 指定区域中心点的经纬度, 以及半径    CLCircularRegion *circular = [[CLCircularRegion alloc] initWithCenter:center radius:500 identifier:@"软件园"];    [self.mgr startMonitoringForRegion:circular];}#pragma mark - CLLocationManagerDelegate// 进入监听区域时调用- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{    NSLog(@"进入监听区域时调用");}// 离开监听区域时调用- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{    NSLog(@"离开监听区域时调用");}//#pragma mark - 懒加载- (CLLocationManager *)mgr{    if (!_mgr) {        _mgr = [[CLLocationManager alloc] init];    }    return _mgr;}@end

地理编码实例

#import "ViewController.h"#import <CoreLocation/CoreLocation.h>@interface ViewController ()#pragma mark - 地理编码/** *  监听地理编码点击事件 */- (IBAction)geocodeBtnClick;/** *  需要编码的地址容器 */@property (weak, nonatomic) IBOutlet UITextField *addressField;/** *  经度容器 */@property (weak, nonatomic) IBOutlet UILabel *longitudeLabel;/** *  纬度容器 */@property (weak, nonatomic) IBOutlet UILabel *latitudeLabel;/** *  详情容器 */@property (weak, nonatomic) IBOutlet UILabel *detailAddressLabel;/** *  地理编码对象 */@property (nonatomic ,strong) CLGeocoder *geocoder;@end@implementation ViewController- (void)geocodeBtnClick{    // 0.获取用户输入的位置    NSString *addressStr = self.addressField.text;    if (addressStr == nil || addressStr.length == 0) {        NSLog(@"请输入地址");        return;    }    // 1.创建地理编码对象    // 2.利用地理编码对象编码    // 根据传入的地址获取该地址对应的经纬度信息    [self.geocoder geocodeAddressString:addressStr completionHandler:^(NSArray *placemarks, NSError *error) {        if (placemarks.count == 0 || error != nil) {            return ;        }        // placemarks地标数组, 地标数组中存放着地标, 每一个地标包含了该位置的经纬度以及城市/区域/国家代码/邮编等等...        // 获取数组中的第一个地标        CLPlacemark *placemark = [placemarks firstObject];//        for (CLPlacemark  *placemark in placemarks) {//            NSLog(@"%@ %@ %f %f", placemark.name, placemark.addressDictionary, placemark.location.coordinate.latitude, placemark.location.coordinate.longitude);        self.latitudeLabel.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];        self.longitudeLabel.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];        NSArray *address = placemark.addressDictionary[@"FormattedAddressLines"];        NSMutableString *strM = [NSMutableString string];        for (NSString *str in address) {            [strM appendString:str];        }        self.detailAddressLabel.text = strM;//        }    }];}- (CLGeocoder *)geocoder{    if (!_geocoder) {        _geocoder = [[CLGeocoder alloc] init];    }    return _geocoder;}@end

反地理编码实例

#import "ViewController.h"#import <CoreLocation/CoreLocation.h>@interface ViewController ()/** *  地理编码对象 */@property (nonatomic ,strong) CLGeocoder *geocoder;#pragma mark - 反地理编码- (IBAction)reverseGeocode;@property (weak, nonatomic) IBOutlet UITextField *longtitudeField;@property (weak, nonatomic) IBOutlet UITextField *latitudeField;@property (weak, nonatomic) IBOutlet UILabel *reverseDetailAddressLabel;@end@implementation ViewController- (void)reverseGeocode{    // 1.获取用户输入的经纬度    NSString *longtitude = self.longtitudeField.text;    NSString *latitude = self.latitudeField.text;    if (longtitude.length == 0 ||        longtitude == nil ||        latitude.length == 0 ||        latitude == nil) {        NSLog(@"请输入经纬度");        return;    }    // 2.根据用户输入的经纬度创建CLLocation对象    CLLocation *location = [[CLLocation alloc] initWithLatitude:[latitude doubleValue]  longitude:[longtitude doubleValue]];    // 3.根据CLLocation对象获取对应的地标信息    [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {        for (CLPlacemark *placemark in placemarks) {            NSLog(@"%@ %@ %f %f", placemark.name, placemark.addressDictionary, placemark.location.coordinate.latitude, placemark.location.coordinate.longitude);            self.reverseDetailAddressLabel.text = placemark.locality;        }    }];}//#pragma mark - 懒加载- (CLGeocoder *)geocoder{    if (!_geocoder) {        _geocoder = [[CLGeocoder alloc] init];    }    return _geocoder;}@end
0 0
原创粉丝点击