iOS开发 - CoreLocation地理定位

标签: 地理定位开发地理编码反地理编码
2150人阅读 评论(0) 收藏 举报
分类:

简介

在移动互联网时代,移动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

//当地理\反地理编码完成时,就会调用CLGeocodeCompletionHandler
typedef 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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:710354次
    • 积分:12729
    • 等级:
    • 排名:第1156名
    • 原创:292篇
    • 转载:1篇
    • 译文:247篇
    • 评论:118条
    博客专栏
    最新评论