定位和地图一直是开发基础最重要的一部分,但是我在日常的开发过程中很少使用到系统原生的定位服务和地图了。所以将相关的内容重新看了一遍,并且将看的过程中迷糊的点记录下来。
1.定位服务
1.1 基本配置
苹果的官方文档中明确的表示:任何使用定位服务的应用都必须声明相关的键值对,对用户解释需要使用定位服务的原因。没有声明会直接无法通过审核。
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
但是在声明的过程中特别需要明白的是:
在Info.plist文件中声明的以上两个键只表示"该应用需要的功能包括定位服务",如果设备中并没有定位支持仍然还能从App Store中下载。但如果应用要求设备必须支持定位服务,那么还需要额外的在Info.plist文件中配置。
/*
App Store使用该键中的值阻止不支持值中声明特性的设备下载该应用
*/
UIRequiredDeviceCapabilities
UIRequiredDeviceCapabilities的值是一组表示你应用要求支持功能的字符串。两个与定位服务相关的字符串:
//应用中要求必须有定位支持
location-services
//应用中要求必须有gps定位服务功能时 --->GPS硬件
gps
当然,如果你的应用不是硬性要求设备支持定位服务,就尽量不要使用该键值对,不然会导致一些不支持定位的设备无法下载应用。
在iOS 11中,对于定位在Info.plist中的配置出现了不同的地方:
如果想要申请Always的权限,按照之前版本的做法配置NSLocationAlwaysUsageDescription之后,并不会有任何的提示,并且会出现警告:
是因为在iOS 11的时候添加了一个新键:
Privacy - Location Always and When In Use Usage Description
为了兼容iOS 11的定位服务,需要特别注意!
1.2 定位服务初步实现
定位最基本的服务就是提供对当前位置的定位以及对用户行为的追踪,整个实现的过程可以划分为几个步骤:
① 判断定位服务是否可获得
[CLLocationManager locationServicesEnabled]
应用无法获得定位服务的原因包括:
1.用户在设置中关闭了定位服务
2.用户关闭对某一个应用的定位服务
3.处于飞行的模式时,设备会无法启动必要的硬件
2.使用标准的定位服务
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
//精确度
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
//水平移动一定距离后再更新定位
self.locationManager.distanceFilter = 500;
//应用处于后台时系统进行电量节省行为的处理设置
self.locationManager.pausesLocationUpdatesAutomatically = YES;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
if (@available(iOS 9.0, *)) {
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
} else {
// Fallback on earlier versions
}
上面的代码,就我个人而言,有几点可以进行解析:
①对distanceFilter和desiredAccuracy属性的区分
结合苹果原生的地图能更直接的明白:
desiredAccuracy定义的是图中的圆半径,圆中的任意一点都有可能是当前定位的坐标点,这就是常说的精确度。而distanceFilter是指再次更新位置信息的距离。
由于移动设备的硬件局限性,所以图中圆半径目前不可能为零,定位总会存在一定的误差。
② 后台省电模式
当然对于授权“总是”的定位服务,即便应用并没有在运行中,苹果也会不断的提示用户:
①在顶部一直显示定位的标识符:
②系统弹框提示"是否允许一直定位"
考虑到当应用处于后台时,用户很容易忽略导致定位一直开启。所以如果应用允许后台定位的话,苹果建议开启“允许系统根据电量暂停定位”
self.locationManager.pausesLocationUpdatesAutomatically = YES;
系统暂停定位的行为除去电量等硬性因素,也允许开发者通过activityType声明定位使用的原因,从而影响系统暂停定位的操作:
在iOS 9之后苹果要求如果应用在Capabilities中添加了后台定位模式,那么必须在代码中设置setAllowsBackgroundLocationUpdates。但是如果没有设置后台定位模式的情况下,将setAllowsBackgroundLocationUpdates设置为YES,应用会直接崩溃。
3.定位数据回调
CLLocationManagerDelegate回调方法均为可选
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations API_AVAILABLE(ios(6.0), macos(10.9));
苹果在标准定位的时候会多次调用该方法,原因:
①之前的缓存定位数据
②在定位的时候,从大精度的范围逐步的缩小到当前的定位范围
官方文档中声称定位到当前位置精度不会超过15秒。但是由于定位返回的数据一般都会优于当前设置的精度值,所以尽量不要设置过小的desiredAccuracy值。
当然,对于上面的问题也提供了几种处理的的方法:
① 判断定位的时间点
NSDate *date = location.timestamp;
NSTimeInterval timeInterval = [date timeIntervalSinceNow];
if (fabs(timeInterval) < 15.0) {
[manager stopUpdatingLocation];
}
}
只有当返回的定位数据距离当前时间不错过15秒,才会认为是新数据。
② 只进行一次定位
如果应用只需一次快速修正精确度的定位,iOS 9开始提供了方法:
[self.locationManager requestLocation];
系统在完成定位回调之后,会自动的停止定位。
1.1 局域监控
区域监控可以分为两种 --->地理位置的监控以及iBeacon的监控。将整个局域监控的实现过程划分为以下几个步骤:
1.判断区域监控服务是否可获得
//地理区域位置的监控
[CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]
//iBeacon的监控
[CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]
2.地理区域位置监控
顾名思义,就是对进入或者离开某一个区域进行监控,强调的"地理区域"指的是一个固定的地理位置。如:当走过“李周洗衣店”时,会提示用户去拿洗好的衣服。
对于监控的回调方法:
//进入某个区域
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
//离开某个区域
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
//判断用户当前与该区域的相对位置
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
在对地理位置的监控过程中,需要注意几点:
① 定位服务授权状态的影响
② 用户与该区域的相对位置
使用该方法
[locationManager requestStateForRegion:region];
可以获得用户和该区域的相对位置。因为如果注册的时候用户已经在区域内时,是不会响应“进入区域”的回调方法。通过调用以下的方法判断用户是否处于某一个区域内。
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
switch (state) {
case CLRegionStateInside:
NSLog(@"已经在该区域内");
break;
case CLRegionStateOutside:
NSLog(@"还在该区域外面");
break;
case CLRegionStateUnknown:
NSLog(@"定位出错");
default:
break;
}
}
如果应用在后台的模式下收到有关注册区域的新信息,则会在应用启动时由launchOptions传递UIApplicationLaunchOptionsLocationKey键。系统会自动开启应用,但是需要手动创建CLLocationManager。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
//需要重新创建一个CLLOcaitonManager并且设置代理收到相关的更新数据
//后台定位服务需要有明确的目的 --->如追踪用户的行为 或者监控区域等
lZLocationManager *manager = [lZLocationManager sharedLocationManager];
[manager startRegionFromBackground];
}
}
所以当应用处于后台模式监听定位服务时,可以通过通知的方式进行提示。
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"新定位到了";
content.subtitle = @"come on!定位来了";
content.body = [NSString stringWithFormat:@"你已经到了 ----%@ %@",placemark.name,placemark.thoroughfare];
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats:NO];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"定位" content:content trigger:trigger];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"%@",error.localizedDescription);
}
}];
③ 区域注册
if (radius > CLLocationDistanceMax) {
radius = CLLocationDistanceMax;
}
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:coordinate radius:radius identifier:identifier];
[locationManager startMonitoringForRegion:region];
应用注册的区域同时间内不能超过20个,所以最好是注册当前用户位置附近的区域。
定位中的iBeacon想在看Core Bluetooth之后再总结,其实对定位服务而言,还是以小细节为主。