iOS 地图 基础 一 :定位
在 iOS 程序开发中 地图功能是普遍存在的,刚好最近都在研究地图的功能。总结一下,“好记性不如烂笔头”。
简介
地图应用主要分为 定位 与 地图 功能。分别对应 CoreLocation 与 MapKit 框架。
- CoreLocation核心定位内容: 【着重功能实现】
1.地理定位(用户个人位置信息等)
2.地理编码(地理编码与反地理编码)
3.区域监听(比如一个区域 监听是否进入或者已经退出 该区域) - MapKit:【着重界面展示】
用于地图展示,例如大头针,导航路线、覆盖层展示等。
下面从简单开始,我们首先了解一下怎么使用CoreLocation进行地图定位的。
定位 (CoreLocation)
CoreLocation 框架的使用(1.地理定位)
CoreLocation框架中所有类的前缀都是CL
CoreLocation中使用 CLLocationManager 对象来做用户定位管理
-
CLLocationManager
@property(assign, nonatomic, nullable) id<CLLocationManagerDelegate> delegate;
// 指定最小更新距离(以米为单位)。设置当用户超过指定的最小更新距离才会返回通知。默认为None,及时更新.
@property(assign, nonatomic) CLLocationDistance distanceFilter;
// 期待的精准度 (可以根据不用的使用场景进行适合的精度值选择,越精准,耗电大)
kCLLocationAccuracyBest
*达到最佳的准确度。
kCLLocationAccuracyBestForNavigation
*用于导航。
...
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
// 导航方向期待精准度 默认为1度就会 回调用户信息
@property(assign, nonatomic) CLLocationDegrees headingFilter __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;
// 其他更多参数解析 参考于: https://m.aliyun.com/yunqi/articles/39192
CLLocationManager 的常用操作
1.开始更新用户位置
- (void)startUpdatingLocation;
[self.locationManager startUpdatingLocation];//开启定位
2.停止更新用户位置
-(void)stopUpdatingLocation;
[self.locationManager stopUpdatingLocation];//开启定位
当调用startUpdatingLocation 方法后,就开始请求、刷新用户的位置,当前用户位置与上一次位置超过规定精准度距离或者开启定位,就会调用以下代理方法:
/**
* 获取到用户位置之后调用
*
* @param manager 位置管理者
* @param locations 位置信息数组
*/
-(void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations;
- locations里面包含了CLLocation定位信息模型。
3.为了严谨起见,最好在使用定位功能之前判断当前应用定位是否可用。判断当前应用的定位功能是否可以用:
+(BOOL)locationServicesEnabled;
-
CLLocation 【定位信息模型】
CLLocation 用来表示某个位置的地理信息,如经纬度、海拔、航向角度、移动速度等。
@interface CLLocation : NSObject <NSCopying, NSSecureCoding>
@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)
... 具体内容... 用到再分析
CLLocation 对象方法 可以计算2个位置之间的距离
-(CLLocationDistance)distanceFromLocation:(constCLLocation*)location;
-
定位权限问题
!要想使用LBS定位,必须先向用户主动申请授权。
从iOS6 开始苹果越来越注重用户的隐私问题,而在定位上 iOS8 、iOS9 都做了相应的规定。比如iOS8开始需要开发者主动向用户申请授权,iOS9 后台定位的设置等。具体解决方法,后面例子中会说明操作。
-
CLLocationCoordinate2D
关于经纬度的知识
使用模拟器自定义定位坐标进行测试,具体操作为:
-
CLGeocoder (2.地理编码 [地理编码与反地理编码])
地理编码:根据给定的地名,获得具体的位置信息。(比如经纬度、地址的全称等)
CLGeocoder 对象方法:
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
反地理编码方法: 根据给定的经纬度,获得具体的位置信息。
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
当进行地理编码、反地理编码调用成功之后,调用(CLGeocodeCompletionHandler)completionHandler ,其结构为:
typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);
error: 返回错误信息
Placemarks:含有CLPlacemark 对象的数组
-
CLPlacemark 存储地标信息模型
@interface CLPlacemark : NSObject <NSCopying, NSSecureCoding> @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; //城市 ...
3.(监听区域)
- 创建监听区域、请求区域
创建监听区域主要设置 (区域中心点与区域半径)
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 判断区域监听服务是否可用(定位服务是否关闭, 定位是否授权, 是否开启飞行模式)
if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
// 创建区域中心
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(29.11111, 131.2222);
// 创建区域(指定区域中心,和区域半径)
CLLocationDistance radius = 1000;
// 判断区域半径是否大于最大监听区域半径,如果大于, 就没法监听。(在前年创建定位的时候,也有设置判断定位区域大小)
if (radius > self.locationM.maximumRegionMonitoringDistance) {
radius = self.locationM.maximumRegionMonitoringDistance;
}
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:@"区域1"];
// 开始监听指定区域
[self.locationM startMonitoringForRegion:region];
// 请求某个区域的状态
[self.locationM requestStateForRegion:region];
}else
{
NSLog(@"区域监听不可用");
}
}
#pragma mark - CLLocationManagerDelegate
// 进去监听区域后调用(调用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didEnterRegion:(nonnull CLRegion *)region
{
NSLog(@"进入区域---%@", region.identifier);
[manager stopMonitoringForRegion:region];
}
// 离开监听区域后调用(调用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didExitRegion:(nonnull CLRegion *)region
{
NSLog(@"离开区域---%@", region.identifier);
}
// 当监听区域失败时调用(监听区域个数是有上限的, 如果大于上限,再创建区域就会失败,就会执行此方法)
-(void)locationManager:(nonnull CLLocationManager *)manager monitoringDidFailForRegion:(nullable CLRegion *)region withError:(nonnull NSError *)error
{
// 一般都是在此处把比较远的区域给移除
// [manager stopMonitoringForRegion:];
}
// 请求某个区域状态时, 回调的代理方法
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
switch (state) {
case CLRegionStateUnknown:
NSLog(@"未知状态");
break;
case CLRegionStateInside:
NSLog(@"在区域内部");
break;
case CLRegionStateOutside:
NSLog(@"在区域外部");
break;
default:
break;
}
}
=======
下面进行一个简单的封装 个人定位 与 获取地理位置信息 的工具类进行分析。
代码:
SLLocationTool.h
//
// SLLocationTool.h
// CoreLoationMapViewDemo
//
// Created by Melody on 2016/12/2.
// Copyright © 2016年 admin. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h> // 1.加载头文件
#import <UIKit/UIKit.h>
#import "Singleton.h" //(封装了生成单例的宏)
typedef void(^ResultBlock) (CLLocation *currentLoc,CLPlacemark *placemark,NSString *error);
@interface SLLocationTool : NSObject
single_interface(SLLocationTool); // 单例,(由于在项目中,可能存在不同地方 多次调用个人定位,所以设置为单例)
/**
获取当前位置
@param block 获取当前位置后处理的block
*/
- (void)getCurrentLocation:(ResultBlock)block;
/**
停止定位
*/
- (void)stopUpdatingLocation;
@end
授权问题
1、在iOS7之前,当用到个人定位的时候,系统会自动提示用户是否允许定位授权。
2、而在iOS8之后,苹果要求开发者主动调用提示用户允许定位授权,而且必须在info.plist 文件中配置一项属性才能弹出授权窗口,分别为
NSLocationWhenInUseDescription ,允许在前台获取GPS的描述
NSLocationAlwaysUsageDescription ,允许在前后台获取GPS的描述
3、如果在iOS9以后,想要在后台获取用户位置:
- 如果当前的授权状态是 前台定位授权,那么你需要勾选后台模式location updates
NSArray *backModes = [infoDict valueForKey:@"UIBackgroundModes"]; // 获取后台参数数组
if ([backModes containsObject:@"location"]) {
self.locaManager.allowsBackgroundLocationUpdates = YES; (设置为YES后 当使用When 授权时,运行在后台不会出现蓝条) // 判断后台模式中是否包含位置更新服务
}
- 如果当前的授权状态是 前后台定位授权,那么默认情况下,就可以在后台获取用户位置信息,不需要勾选后台模式 (location updates)
SLLcoationTool 简单的进行获取个人定位的位置与信息 Demo :
[[SLLocationTool sharedSLLocationTool] getCurrentLocation:^(CLLocation *currentLoc, CLPlacemark *placemark, NSString *error) {
if ([error length] == 0) {
NSLog(@"%@ ---- %@", currentLoc, placemark.name);
NSLog(@"%f : %f",currentLoc.coordinate.latitude,currentLoc.coordinate.longitude);
NSLog(@"%@",placemark.locality);
}
}];
或者:
[[SLLocationTool sharedSLLocationTool] getCurrentLocation:^(CLLocation *currentLoc, CLPlacemark *placemark, NSString *error) {
if ([error length] == 0) { //定位获取授权成功
// NSLog(@"%f : %f",currentLoc.coordinate.latitude,currentLoc.coordinate.longitude);
if (placemark.name) NSLog(@"%@",placemark.name);
}else { // 定位获取授权失败
//情景一:一旦用户选择了“Don’t Allow”,意味着你的应用以后就无法使用定位功能,且当用户第一次选择了之后,以后就再也不会提醒进行设置。所以当第一次被拒绝事,可以通过代码跳转到设置界面。
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:error message:@"用户拒接定位,请在设置-私隐-中允许app授权定位" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSURL *url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:url]) { //打开设置的URL
if (kDeviceSysVersion < 10.0) {
[[UIApplication sharedApplication] openURL:url];
}else {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
}
}];
[alertVC addAction:cancelAction];
[self.window.rootViewController presentViewController:alertVC animated:YES completion:nil];
}
}];
!! 要注意的是, 获取个人定位信息是异步的,如果在程序中的逻辑需要 使用到 个人定位信息,要确保在 使用前 个人定位信息 是否存在。
具体代码已经 post 上 Github:https://github.com/NeverLand-LLT/CoreLocationDemo
感谢你们的阅读,欢迎来纠错与交流。