本篇主要讲解location framework,同样也是本系列的最后一小节。
location framework介绍
location framework是什么
- location framework是一个非常有用的框架,主要目的用来获取GPS接收设备的数据,其中包括:坐标、速度、(地点的)标题、运动轨迹;
- 这个框架被大量使用,不仅仅在自带的地图中,同样还在很多其余的应用中(包括你即将开发的应用)、运动类型的应用、社交类型的应用(微博、微信等);
- 这个框架是可编程的,所以可以配置和定义框架提供的数据、定义区域和边界、定义两点间的距离、作为后台进程执行;
location manager
location manager是我们用来接收GPS数据的组件。为了使用这个组件,我们需要做如下工作:
- 添加corelocation.framework组件;
- 在ViewController.h中import CoreLocation.h;
- 添加CLLocationManager *locationManager属性;
- 设置ViewController实现CLLocationManagerDelegate代理(注意头文件和viewDidLoad都要设置);
- 编写初始化代码:
- (void)viewDidLoad { [super viewDidLoad]; // init self.locationManager = [CLLocationManager new]; [self.locationManager setDelegate: self]; // config [self.locationManager setDesiredAccuracy: kCLLocationAccuracyBest]; [self.locationManager setDistanceFilter: kCLDistanceFilterNone]; // start service [self.locationManager startUpdatingLocation]; }
- 编写代理方法:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { for (NSObject *obj in locations) { CLLocation *loc = (CLLocation *) obj; // coordination NSLog(@"lat: %f, lng: %f", loc.coordinate.latitude, loc.coordinate.longitude); // location NSLog(@"%@", loc); } }
- 完成,查看结果。
启动和停止location manager
之前我们已经做了部分启动和停止的工作,所以我们需要做的只是简单的重构下我们的代码。
#import "ViewController.h"
@interface ViewController ()
-(void)startLocationService;
-(void)stopLocationService;
@end
@implementation ViewController
@synthesize locationManager;
@synthesize locationLabel;
@synthesize loc;
@synthesize timer;
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)startLocationService {
// init
if (self.locationManager == nil) {
self.locationManager = [CLLocationManager new];
}
[self.locationManager setDelegate: self];
// config
[self.locationManager setDesiredAccuracy: kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter: kCLDistanceFilterNone];
// start service
[self.locationManager startUpdatingLocation];
// refresh periodic
if (timer == nil) {
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(refreshCoordination) userInfo:nil repeats:YES];
}
}
-(void)refreshCoordination {
NSString *txt = [[NSString alloc] initWithFormat: @"location is lat:%f, lng: %f", loc.coordinate.latitude, loc.coordinate.longitude, nil];
[locationLabel setText: txt];
}
-(void)stopLocationService {
[self.locationManager stopUpdatingLocation];
[self.locationManager setDelegate: nil];
if (timer != nil) {
[timer invalidate];
timer = nil;
}
[locationLabel setText: @"Location Manager stopped."];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
for (NSObject *obj in locations) {
loc = (CLLocation *) obj;
// coordination
NSLog(@"lat: %f, lng: %f", loc.coordinate.latitude, loc.coordinate.longitude);
// location
NSLog(@"%@", loc);
}
}
- (IBAction)startService:(id)sender {
[self startLocationService];
}
- (IBAction)stopService:(id)sender {
[self stopLocationService];
}
代码中使用按钮来控制启动和停止服务。
不同的定位技术
众所周知,定位技术实际上是基于以下几种方案来实现的:
- 蜂窝网络:精度较低,但是用的电池较少。
- Wi-Fi定位:由于城市中有很多热点,所以在室内可以使用,而且可以在很多仅有WIFI的设备上使用(例如iPod),支持漫游。
- GNSS:全球卫星导航系统,他消耗的电源是最多的(因为要使用单独的定位模块),代表包括NavStar、GLONASS等等;
Location Manager使用总结
注册location manager delegate
- 在.h头文件中声明代理CLLocationManagerDelegate;
- 在实现代码中添加设置delegate代码:
-(void)startLocationService { // init if (self.locationManager == nil) { self.locationManager = [CLLocationManager new]; } [self.locationManager setDelegate: self]; if ([self conformsToProtocol:@protocol(CLLocationManagerDelegate)]) { NSLog(@"Set to receive location updates!"); // config [self.locationManager setDesiredAccuracy: kCLLocationAccuracyBest]; [self.locationManager setDistanceFilter: kCLDistanceFilterNone]; // start service [self.locationManager startUpdatingLocation]; // refresh periodic if (timer == nil) { timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(refreshCoordination) userInfo:nil repeats:YES]; } } }
判断当前location service是否可用
代码如下:
if ([CLLocationManager locationServicesEnabled]) {
NSLog(@"Service is enabled.");
} else {
NSLog(@"Service is down?");
}
检查错误
为了捕获操作中可能出现的错误,需要实现代理方法:
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(@"%@", [error description]);
}
服务开启、关闭方法
正常的服务开启、关闭使用:
// start service
[self.locationManager startUpdatingLocation];
// stop service
[self.locationManager stopUpdatingLocation];
低精度的开启、关闭方法,一般用在程序切换到后台时:
// start service
[self.locationManager startMonitoringSignificantLocationChanges];
// stop service
[self.locationManager stopMonitoringSignificantLocationChanges];
使用位置信息
开启位置信息代码:
-(void)startLocationService {
// init
if (self.locationManager == nil) {
self.locationManager = [CLLocationManager new];
}
[self.locationManager setDelegate: self];
if ([self conformsToProtocol:@protocol(CLLocationManagerDelegate)]) {
NSLog(@"Set to receive location updates!");
// determine if metric system is used
if ([[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem]) {
self.isMetric = YES;
} else {
self.isMetric = NO;
}
// initialize variables for duration and distance
self.totalDuration = 0.0;
self.totalDistance = 0.0;
// config
// define the amount of accuracy to be used
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
// set a distance filter
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
// start service
[self.locationManager startUpdatingLocation];
// onboard hardware compass only
// determine if heading is available
if ([CLLocationManager headingAvailable]) {
self.locationManager.headingFilter = 5;
[self.locationManager startUpdatingHeading];
} else {
[self.headingTitle setHidden:YES];
}
}
}
刷新位置信息代码:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
// horizontal accuracy
if (newLocation.horizontalAccuracy < self.locationManager.desiredAccuracy) {
// return;
}
// time
NSString *now = [[NSString alloc] initWithFormat:@"%@", newLocation.timestamp];
NSString *timeString = [[now componentsSeparatedByString:@" "] objectAtIndex:1];
self.timeLabel.text = timeString;
// create an oldLocation variable if one doesn't exist
if (self.oldLocation == nil) {
self.oldLocation = newLocation;
}
// duration
NSTimeInterval distanceBetweenDates = [newLocation.timestamp timeIntervalSinceDate:self.oldLocation.timestamp];
self.totalDuration += distanceBetweenDates;
double minutes = self.totalDuration/60.0;
CGFloat seconds = (float)((int)self.totalDuration % (int)60.0);
self.durationMinutes.text = [@"" stringByAppendingFormat:@"%d", (int)minutes];
self.durationText.text = [@"" stringByAppendingFormat:@"%d", (int)seconds];
// distance
self.latitudeLabel.text = [NSString stringWithFormat:@"%f", newLocation.coordinate.latitude];
self.longitudeLabel.text = [NSString stringWithFormat:@"%f", newLocation.coordinate.longitude];
CLLocationDistance distance = [newLocation distanceFromLocation:self.oldLocation];
if (distance != -1) {
self.distanceText.text = [@"" stringByAppendingFormat:@"%d", (int)distance];
}
self.totalDistance += distance;
// update value of oldLocation
self.oldLocation = newLocation;
if ([newLocation verticalAccuracy] < 1.0) {
// return;
}
// speed-course-altitude
self.speedLabel.text = [NSString stringWithFormat:@"%d", (int)[newLocation speed]];
self.courseLabel.text = [NSString stringWithFormat:@"%d", (int)[newLocation course]];
self.latitudeLabel.text = [NSString stringWithFormat:@"%d", (int)[newLocation altitude]];
}
调试中有个问题:
didUpdateToLocation方法没有即时刷新的情况,主要在真机上,模拟器上会不停调用的。这种问题目前没找到合理的处理方法,但我在另外的真机上时又没有遇到同样的问题,所以可能是个案?
监控进入和退出某一区域
这一部分代码比较简单,使用的仍然是代理方法:
#define RADIUS 1000
@interface ViewController ()
-(void)startLocationService;
-(void)stopLocationService;
@end
@implementation ViewController
@synthesize locationManager;
@synthesize region;
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)startLocationService {
// init if not exist
if (self.locationManager == nil) {
self.locationManager = [[CLLocationManager alloc] init];
}
// set delegate
[self.locationManager setDelegate: self];
if ([self conformsToProtocol:@protocol(CLLocationManagerDelegate)]) {
// define the amount of accuracy to be used
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
// set a distance filter
[self.locationManager setDistanceFilter: kCLLocationAccuracyHundredMeters];
if ([CLLocationManager regionMonitoringAvailable] == YES) {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
CLLocationCoordinate2D point = CLLocationCoordinate2DMake(31.205042, 121.460631);
CLLocationDistance radius = RADIUS;
if (RADIUS > [self.locationManager maximumRegionMonitoringDistance]) {
radius = self.locationManager.maximumRegionMonitoringDistance;
};
region = [[CLRegion alloc] initCircularRegionWithCenter:point radius:radius identifier:@"YouYi ShiDai"];
[self.locationManager startMonitoringForRegion:region];
}
}
}
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(@"You are in the region.");
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(@"You are running away.");
}
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
NSLog(@"There's sth. wrong.");
}
-(void)stopLocationService {
// stop service
[self.locationManager stopMonitoringForRegion:region];
[self.locationManager setDelegate: nil];
}
计算两点间距离
仅能计算两点间球面距离,不包括海拔等信息。
- (IBAction)calculate:(id)sender {
CLLocation *shanghai = [[CLLocation alloc] initWithLatitude:31.14 longitude:121.29];
CLLocation *beijing = [[CLLocation alloc] initWithLatitude:39.55 longitude:116.24];
CLLocationDistance distance = [shanghai distanceFromLocation:beijing];
[distanceLabel setText: [[NSString alloc] initWithFormat:@"%f", distance]];
}
通过GEO反查地名
CLLocation *shanghai = [[CLLocation alloc] initWithLatitude:31.14 longitude:121.29];
CLGeocoder *coder = [[CLGeocoder alloc] init];
[coder reverseGeocodeLocation:shanghai completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error) {
NSString *str = [[NSString alloc] initWithFormat:@""];
if ([placemarks count] > 0) {
for (CLPlacemark *placemark in placemarks) {
str = [str stringByAppendingFormat:@"%@ %@ %@ %@", @"\n", placemark.name, @", ", placemark.administrativeArea];
}
NSLog(str);
}
}
}];
通过地名查找坐标
NSString *location = @"shanghai";
[coder geocodeAddressString:location completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error) {
if (placemarks.count > 0) {
for (CLPlacemark *placemark in placemarks) {
NSLog([@"" stringByAppendingFormat:@"location lat:%f, lng:%f.", placemark.location.coordinate.latitude, placemark.location.coordinate.longitude]);
};
};
}
}];
本系列完结。