使用Core Location获取用户的位置

在使用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类的实例,然后设置它的desiredAccuracydistanceFilter属性。为了能够接受到定位事件的通知,还要给这个实例的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中没有这两项存在,即使调用requestAlwaysAuthorizationrequestWhenInUseAuthorization方法请求启动定位服务,系统也会忽略请求。调用requestAlwaysAuthorization需要info.plist中有NSLocationAlwaysUsageDescription,调用requestWhenInUseAuthorization需要info.plist中有NSLocationWhenInUseUsageDescription

这两项只有在iOS 8.0之后的设备上会起作用,在iOS 6.0到iOS 8.0的设备上,使用NSLocationUsageDescription来请求启用定位服务。

经纬度逆向解析

一般定位系统所返回的位置信息都是经纬度,将经纬度逆向解析就可以得到我们的需要的地址。

在Core Location框架下,使用CLGeocoderCLPlacemark就可以将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

你可能感兴趣的:(corelocation)