在使用Core Location之前,我们必须将CoreLocation.framework
链接到Xcode项目中。在需要使用Core Location的源文件的中,导入框架#import
。
获取用户当前位置
Core Location能够让我们定位当前的位置,并在App中使用位置信息。它会记录设备位置,并且可以进行周期性更新。它的所作所为,均取决于我们对定位服务的设置。
有两种服务可以获取当前位置:
standard location service。 它是可配置,可以满足基本需求的解决方案。可以获取位置信息,根据精度设置跟踪位置变化。
significant-change location service。 只有当设备的位置有大的改变时,比如移动500米或者更远,才会提交更新请求。
收集位置信息是一个非常耗电的操作。无论位置信息在你的App里是多么重要,我们都应该合理地设置它、使用它,避免设备的点亮被大量消耗。如果,你的App即使在后台也需要对位置进行管理的话,在info.plist中,添加UIBackgroundModes
字段(它是一个数组类型),加入location
值。这时,将location manager的pausesLocationUpdatesAutomatically
属性设为YES
,以节省电量。比如,与健康、路线规划导航相关的App可能会使用这样的定位服务设置。
判断定位服务是否可用
在某些情况下,定位服务可能不可用。比如:
用户在系统设置中禁用了定位服务。
用于拒绝了某个App使用定位服务。
因此,在尝试启用定位服务之前,应该调用CLLocationManager
的类方法locationServicesEnable
。这个方法用来判断系统的定位服务是否开发。如果返回NO
,当你尝试开启定位服务时,系统会提示用户是否重新启用定位服务。
启用Standard Location Service
standard location service是获取用户当前位置最常用的方式,因为所有的iOS和OS设备都可以使用。在使用之前,你需要对相关参数进行设置。比如指定位置数据的精度和重新获取位置信息的移动距离。当启动服务时,它会根据设置的参数决定硬件的工作方式,然后把定位相关的事件提交给你的App。标准定位服务非常适合那些位置信息精度要求很高的App(比如说导航类的App),因为它可以让定位跟踪硬件长时间工作,随之而来的就是电量的高消耗。
要使用标准定位服务,需要创建一个CLLocationManager
类的实例,然后设置它的desiredAccuracy
和distanceFilter
属性。为了能够接受到定位事件的通知,还要给这个实例的delegate
赋值。最后,调用startUpdatingLocation
方法。一旦获取到位置信息,location manager就会通知它的delegate。如果最新的位置信息被提交到App,你可以直接从CLLocationManager
对象获取到最近一次的位置信息。调用location manager对象的stopUpdatingLocation
方法可以停止提交位置信息。
接下来,给出一段代码,按照上面的步骤启用标准定位服务。
- (void)startStandardUpdates {
//如果locactionManager未初始化,则进行初始化
if (self.locationManager == nil) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
//设置`精度`和`触发更新事件的最短水平距离`
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 5;
//开启标准定位服务
[self.locationManager startUpdatingLocation];
}
distanceFilter
属性在更新位置之前设备需要移动的最短水平距离,单位是米。上述代码值为5,表示至少移动5米才会更新一次位置信息。它的默认值是kCLDistanceFilterNone
,表示移动任何距离都会更新一次位置信息,即使不移动依然会更新位置信息。
启用Significant-Change Location Service
启用significant-change location service时,同样需要创建CLLocationManager
类的对象,然后给它的delegate赋值,调用startMonitoringSignificantLocationChanges
方法就可以了。
注意:使用这个服务时,CLAuthorizationStatus的值要设为kCLAuthorizationStatusAuthorizedAlways,使App可以在任何时间使用定位服务。
- (void)startSignificantChangeUpdates
{
if (self.locationManager == nil)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
}
接收位置信息
无论使用哪一种定位服务,接收位置数据的方法都是一样的。当位置数据可取时,location manager会调用locationManager:didUpdateLocations:
代理方法。在接收数据出错时,location manager会调用locationManager:didFailWithError:
代理方法。
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation* location = [locations lastObject];
NSDate* eventDate = location.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
//根据取到的location的时间戳,输出经纬度
if (fabs(howRecent) < 15.0) {
NSLog(@"latitude %+.6f, longitude %+.6f\n",
location.coordinate.latitude,
location.coordinate.longitude);
}
}
CLLocation
对象除了时间戳之外,还包括
坐标_coordinate_。有经度和纬度。
海拔_atitude_。正数表示在海平面之后,负数表示在海平面之下。
楼层数_floor_。如果无法获取,则被置为nil。
水平精度_horizontalAccuracy_。单位是米。以经纬度为中心的圆的半径。如果为负数,说明经纬度无效。
垂直精度_verticalAccuracy_。单位是米。表示海拔上的误差。如果为负数,说明经纬度无效。这个属性需要iOS设备有GPS功能。因此,在老一批的iOS设备上,这个值永远会返回负数。
_description_。它是一个格式化的字符串,包含CLLocation对象可以包含的所有属性的值。使用NSLog输出CLLoctation对象时,就是输出这个属性的值。和一些类的description方法是一样的东西。重写CLLocation的
(NSString*)description
方法,可以自定义输出格式。
NSLocationAlwaysUsageDescription和NSLocationWhenInUseUsageDescription
这两项是需要添加到info.plist中的。如果info.plist中没有这两项存在,即使调用requestAlwaysAuthorization
和requestWhenInUseAuthorization
方法请求启动定位服务,系统也会忽略请求。调用requestAlwaysAuthorization
需要info.plist中有NSLocationAlwaysUsageDescription
,调用requestWhenInUseAuthorization
需要info.plist中有NSLocationWhenInUseUsageDescription
。
这两项只有在iOS 8.0之后的设备上会起作用,在iOS 6.0到iOS 8.0的设备上,使用NSLocationUsageDescription
来请求启用定位服务。
经纬度逆向解析
一般定位系统所返回的位置信息都是经纬度
,将经纬度
逆向解析就可以得到我们的需要的地址。
在Core Location框架下,使用CLGeocoder
和CLPlacemark
就可以将CLLocation中的信息解析成地址。
拿上面的代理方法中得到的CLLocation实例做解析对象。
CLGeocoder* geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
NSLog(@"%@",[placemarks lastObject].name);
}];
先看一下输出的结果。
2016-03-08 15:58:03.076 latitude +30.283291, longitude +120.115639
2016-03-08 15:58:09.841 西湖国际科技大厦D座
CLPlacemark还有许多与该地址相关的属性,可以输出看一下。
2016-03-08 16:04:09.645 name:西湖国际科技大厦D座
2016-03-08 16:04:09.646 ISOcountryCode:CN
2016-03-08 16:04:09.646 country:中国
2016-03-08 16:04:09.647 postalCode:(null)
2016-03-08 16:04:09.647 administrativeArea:浙江省
2016-03-08 16:04:09.647 subAdministrativeArea:(null)
2016-03-08 16:04:09.647 locality:杭州市
2016-03-08 16:04:09.647 subLocality:西湖区
2016-03-08 16:04:09.647 thoroughfare:文二路391西湖国际科技大厦
2016-03-08 16:04:09.648 subThoroughfare:(null)
2016-03-08 16:04:09.648 region:CLCircularRegion (identifier:'<+30.28099700,+120.12021500> radius 197.16', center:<+30.28099700,+120.12021500>, radius:197.16m)
2016-03-08 16:04:09.649 timeZone:Asia/Shanghai (GMT+8) offset 28800
在结果中我们可以看出:
name:地址名称
ISOcountryCode:国家代码
country:国家
administrativeArea:省份
locality:所属城市
subLocality:所属城市中的区、县等
thoroughfare:带街道的地址名称
timeZone:时区
参考文档
Core Location Framework Reference
Getting the User’s Location