IOS开发学习笔记(十八)——使用地理位置及地图(下篇)

本篇主要讲解location framework,同样也是本系列的最后一小节。


location framework介绍

location framework是什么

  1. location framework是一个非常有用的框架,主要目的用来获取GPS接收设备的数据,其中包括:坐标、速度、(地点的)标题、运动轨迹;
  2. 这个框架被大量使用,不仅仅在自带的地图中,同样还在很多其余的应用中(包括你即将开发的应用)、运动类型的应用、社交类型的应用(微博、微信等);
  3. 这个框架是可编程的,所以可以配置和定义框架提供的数据、定义区域和边界、定义两点间的距离、作为后台进程执行;

location manager

location manager是我们用来接收GPS数据的组件。为了使用这个组件,我们需要做如下工作:

  1. 添加corelocation.framework组件;
  2. 在ViewController.h中import CoreLocation.h;
  3. 添加CLLocationManager *locationManager属性;
  4. 设置ViewController实现CLLocationManagerDelegate代理(注意头文件和viewDidLoad都要设置);
  5. 编写初始化代码:
    - (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];
    }

  6. 编写代理方法:
    - (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);
        }
    }

  7. 完成,查看结果。

启动和停止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];
}

代码中使用按钮来控制启动和停止服务。


不同的定位技术

众所周知,定位技术实际上是基于以下几种方案来实现的:

  1. 蜂窝网络:精度较低,但是用的电池较少。
  2. Wi-Fi定位:由于城市中有很多热点,所以在室内可以使用,而且可以在很多仅有WIFI的设备上使用(例如iPod),支持漫游。
  3. GNSS:全球卫星导航系统,他消耗的电源是最多的(因为要使用单独的定位模块),代表包括NavStar、GLONASS等等;

Location Manager使用总结

注册location manager delegate

  1. 在.h头文件中声明代理CLLocationManagerDelegate;
  2. 在实现代码中添加设置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]);
                };
            };
        }
    }];


本系列完结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值