使用CoreLocation定位
CoreLocation框架 (CoreLocation.framework)可用于定位设备当前经纬度, 通过该框架, 应用程序可通过附近的蜂窝基站\WIFI信号
或者GPS等信息计算用户位置.
iOS SDK提供了CLLocationManager、CLLocationManagerDelegate来处理设备的定位信息,包括获取设备的方向以及进行方向检测等。
其中CLLocationManager是整个CoreLocation框架的核心,定位、方向检测、区域检测等都由该API完成;而CLLocationManagerDelegate是
一个协议,实现该协议的对象可作为CLLocationManager的delegate对象,负责处理CLLocationManager的定位、方向检测、区域检测等相关事
件。iOS还提供了CLLocation (代表位置信息)、CLHeading(代表设备方向)、CIRegion(代表区域)等API,这些API与CLLocationManager、CLLocationManagerDelegate共同组成了iOS的CoreLocation框架.
iOS的定位支持
GPS卫星定位
GPS是英文Global Positing System (全球定位系统) 的简称,由GPS卫星组成的空间部分、若干地面站组成的控制部分和普通用
户手中的接收机这3个部分。
iOS的GPS定位于单纯的GPS定位不同,它是A-GPS(所谓辅助GPS),首先通过基站定位或WiFi定位获得该设备的大概位置,然
后通过将设备的大致位置发发哦远程服务器,由服务器负责进行查询和计算,从而获取当前位置的卫星信息,并通过网络将这些卫星
信息反馈给iOS设备,避免了iOS设备直接通过GPS扫面、分析天上的卫星信息。
基站定位
每个手机基站都是一个标识符,iOS设备可以搜集周围所有收到信号的基站和它们的标识符,通过联网发送到苹果云端服务器,再由
服务器根据这些基站的位置信息查询并计算出当前位置,然后把该定位信息返回给手机。
WiFi定位
iOS设备通过无线网卡搜集周围所有的WiFi热点,获得它们的MAC地址,然后通过Apple的云端服务器查询该WiFi热点是否已经登记,
如果已经登记,即可获取该WiFi热点的位置,最后通过对多个WiFi热点折中计算得到当前位置并返回给iOS设备。精确度大概在几十米范围内。
获取定位信息
CoreLocation.framework的常用API主要有如下几个。 |
|
CLLocationManager |
定位管理类 |
CLLocationManagerDelegate |
该协议代表定位管理器的delegate协议.实现协议的对象可负责处理CLLocationManager的定位事件 |
CLLocation |
该对象代表位置。该对象包含了当前设备的经度、纬度、高度、速度、路线等信息,还包含了该定位信息的水平精确度、垂直精确度以及时间戳信息。 |
CLHeading |
该对象代表设备的移动方向。 |
CLRegion |
该对象代表一个区域。一般程序不会直接使用该类,而是使用它的两个子类,即CLCircularRegion (圆形区域)和CLBeaconRegion (蓝牙信号区)。 |
CLLocationCoordinate2D |
该结构体变量包含经度、纬度两个值。其中CLLocation对象的coordinate属性就是一个CLLocationCoordinate2D结构体变量。 |
获取位置信息
使用CoreLocation..framework进行定位只要如下3步即可 |
|
CLLocationManager负责获取定位信息,delegate则负责处理定位事件--------通过这些事件即可获取设备所在位置。 |
CLLocationManager还提供了如下类方法来判断当前设备的定位相关服务状态。 |
+ locationServicesEnabled:返回当前定位服务是否可用 |
+ deferredLocationUpdatesAvailable:返回延迟定位更新是否可用 |
+ significantLocationChangeMonitoringAvailable:返回重大位置改变监听是否可用。 |
+ headingAvailable:返回该设备是否支持磁力计计算方向。 |
+ isRangingAvailable:返回蓝牙信号范围服务是否可用。 |
CLLocationManager该对象设置如下属性 |
pausesLocationUpdatesAutomatically:设置iOS设备是否可暂停定位来节省电池的电量。如果该属性设为“ YES ”,则当iOS设备不再需要定位数据时,iOS设备可以自动暂停定位 |
distanceFilter: 设置CLLocationManager的自动过滤距离。也就是说,只有当设备在水平方向的位置改变超过该数值(以米为单位)指定的距离时才会生成一次位置改变的信号。 |
desiredAccuracy:设置定位服务的精度。该属性值支持kCLLocationAccuracyBestForNavigation (导航级的最佳精确度)、kCLLocationAccuracyBest(最佳精确度)、kCLLocationAccuracyNearestTenMeters(10米误差)、kCLLocationAccuracyHundredMeters(百米误差)、kCLLocationAccuracyKilometer(千米误差)、kCLLocationAccuracyThreeKilometers(三千米误差)等常量值。当然,也可直接指定一个浮点数作为定位服务允许的误差。 |
activityType:设置定位数据的用途。该属性支持CLActivityTypeOther(定位数据作为普通用途)、CLActivityTypeAutomotiveNavigation(定位数据作为车辆导航使用)、CLActivityTypeFitness(定位数据作为步行导航使用)和CLActivityTypeOtherNavigation(定位数据作为其他导航使用)这几个枚举值之一。 |
代 码 片 段 |
1 ViewController.m 2 3 @interface ViewController () <CLLocationManagerDelegate> 4 5 @property (strong, nonatomic)CLLocationManager* locationManager; 6 7 @end 8 9 @implementation ViewController 10 11 - (void) viewDidLoad 12 13 { 14 15 [super viewDidLoad]; 16 17 // 创建CLLocationManager对象 18 19 self.locationManager = [[CLLocationManager alloc] init]; 20 21 } 22 23 - (IBAction)bnTapped: (id)sender 24 25 { 26 27 // 如果定位服务可用 28 29 if([CLLocationManager locationServiceEnabled]) 30 31 { 32 33 NSLog(@” 开始执行定位服务 ”); 34 35 // 设置定位精度: 最佳精度 36 37 self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; 38 39 // 设置距离过滤器为50米,表示每移动50米更新一次位置 40 41 self.locationManager.distanceFilter = 50; 42 43 // 将视图控制器自身设置为CLLocationManager的delegate 44 45 // 因此该视图控制器需要实现CLLocationManagerDelegate协议 46 47 self.locationManager.delegate = self; 48 49 // 开始监听定位信息 50 51 [self.locationManager startUpdatingLocation]; 52 53 } 54 55 else 56 57 { 58 59 NSLog(@” 无法使用定位服务! ”); 60 61 } 62 63 } 64 65 // 成功获取定位数据后将会激发该方法 66 67 - (void)locationManager: (CLLocationManager*)manager 68 69 didUpdateLocations: (NSArray*)locations 70 71 { 72 73 // 获取最后一个定位数据 74 75 CLLocation* location = [location lastObject]; 76 77 // 依次获取CLLocation中封装的经度、纬度、高度、速度、方向等信息 78 79 self.longitudeTxt.text = [NSString stringWithFormat: @”%g”, location.Coordinate.longitude]; // 经度 80 81 self.latitudeTxt.text = [NSString stringWithFormat: @”%g”, location.Coordinate.latitude]; // 纬度 82 83 self.altitudeTxt.text = [NSString stringWithFormat: @”%g”, location.altitude]; // 高度 84 85 self.speedTxt.text = [NSString stringWithFormat: @”%g”, location.speed]; // 速度 86 87 self.courseTxt.text = [NSString stringWithFormat: @”%g”, location.course]; // 速度 88 89 } 90 91 // 定位失败时激发的方法 92 93 - (void)locationManager: (CLLocationManager*)manager 94 95 didFailWithError: (NSError*)error 96 97 { 98 99 NSLog(@”定位失败: %@”, error); 100 101 } 102 103 @end |
上面程序中的第1段红色字代码为CLLocationManager对象设置了属性之后,关键是将该视 控制器设置为CLLocationManager的delegate,程序调用CLLocationManager的 startUpdatingLocation方法开始获取定位数据. 由于程序指定该视图控制器作为CLLocationManager的delegate, 因此该视图控制器需要实现CLLocationManagerDelegate协议, 并实现该协议中定位相关的两个事件处理方法。当程序成功获取定位数据时,将会激发delegate的locationManager:didUpdateLocation: 方法,因此上面视图控制器类实现了该方法,并在该方法中获取最后一个定位数据:CLLocation对象。 |
CLLocation对象中包含如下属性,这些属性包含了定位相关信息 |
altitude:该属性表示当前设备的海拔高度,单位是米。 |
coordinate:该属性返回一个CLLocationCoordinate2D结构体变量,该结构体变量中包含经度、纬度信息。 course:该属性表示当钱设备前进的方向。该值为0°表示向北,90°表示向东,180°表示向南,270°表示向西。 horizontalAccuracy:该属性表明定位信息的水平精确度。将返回的坐标作为圆心,并将水平精确度视为半径。真正的设备位置落在此圆内的某处。此圆越小,位置就越精确:此圆越大,则位置越不精确。如果精确度为负值,则表明测量精确度失败。 verticalAccuracy:该属性表明定位信息的垂直精确度。也就是说,iOS设备的实际高度在该定位信息的高度加或减该属性值的范围内. |
timestamp:该属性返回定位信息的返回时间. |
speed:该属性返回设备的移动速度,单位是米/秒.实际上,该属性适用于行车速度,而不太适用于步行速度。 |
使用iOS模拟器模拟位置
iOS模拟器本身并不能作为GPS接收机,因此无法得到定位信息,但为了方便程序员测试定位应用,iOS模拟器可以模拟定位信息。
启动iOS模拟器之后,即可通过iOS模拟器主菜单中的“调试”→”位置”来模拟iOS设备的位置
自定位置:可以自行输入位置的经度值、纬度值 |
City Bicycle Ride:模拟设备携带者在城市中骑车移动 |
City Run:模拟设备携带者在城市中跑动。 |
Freeway Drive:模拟设备携带者在高速公路中驾车 |
监控行车速度和行车距离
通过一个示例来计算设备的平均移动速度 下面是实现部分代码 |
1 ViewController.m 2 3 #import “ViewController.h” 4 5 #import <CoreLocation/CoreLocation.h> 6 7 @interface ViewController()<CLLocationManagerDelegate> 8 9 @property (nonatomic, retain) CLLocationManager* locationManager; 10 11 @property (nonatomic, retain) CLLocation* prevLocation; 12 13 @property (nonatomic, assign) CGFloat sumDistance; 14 15 @property (nonatomic, assign) CGFloat sumTime; 16 17 @end 18 19 @implementation ViewController 20 21 - (void) viewDidLoad 22 23 { 24 25 [super viewDidLoad]; 26 27 // 创建CLLocationManager对象 28 29 self.locationManager = [[CLLocationManager alloc] init]; 30 31 } 32 33 - (void)viewWillAppear: (BOOL)animated 34 35 { 36 37 [super viewWillAppear: animated]; 38 39 // 设置定位精度:最佳精度 40 41 self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; 42 43 // 设置距离过滤器为50米,表示每移动50米更新一次位置 44 45 self.locationManager.distanceFilter = 50; 46 47 // 将视图控制器自身设置为CLLocationManager的delegate 48 49 // 因此该视图控制器需要实现CLLocationManagerDelegate协议 50 51 self.locationManager.delegate = self; 52 53 // 开始监听定位信息 54 55 [self.locationManager startUpdatingLocation]; 56 57 NSLog(@” 开始执行定位服务 ”); 58 59 } 60 61 // 定位失败时激发的方法 62 63 - (void)locationManager: (CLLocationManager*)manager 64 65 didFailWithError: (NSError*) error 66 67 { 68 69 NSLog(@”定位失败: %@”, error); 70 71 } 72 73 // 成功获取定位数据后将会激发该方法 74 75 - (void)locationManager: (CLLocationManager*) manager 76 77 didUpdateLocations: (NSArray*)locations 78 79 { 80 81 // 获取最后一个定位数据 82 83 CLLocation* newLocation = [location lastObject]; 84 85 if(newLocation,horizontalAccuracy < kCLLocationAccuracyHundredMeters) 86 { 87 if(self.prevLocation) 88 { 89 90 // 计算本次定位数据与上次定位数据之间的时间差 91 92 NSTimerInterval dTime = [ newLocation.timestamp timeIntervalSinceDate: self.prevLocation.timestamp]; 95 96 // 累计行车时间 97 98 self.sumTime += dTime; 99 100 // 计算本次定位数据与上次定位数据之间的距离 101 102 CGFloat distance = [newLocation distanceFromLocation:self.prevLocation]; 105 106 // 如果距离小于1米,则忽略本次数据,直接返回该方法 107 108 if(distance < 1.0f) 110 { 112 return; 114 } 115 116 // 累加移动距离 118 self.sumDistance += distance; 120 // 计算移动速度,将米/秒换算成千米/小时,需要乘以3.6 122 CGFloat speed = distance / dTime * 3.6; 124 // 计算平均速度 126 CGFloat avgSpeed = self.sumDistance / self.sumTime * 3.6; 128 NSString* speedFeedback = [NSString stringWithFormat: 130 @”当前速度为: %g千米/小时, 平均速度为: %g千米/小时。合计移动:%g千米”, speed, avgSpeed,self.sumDistance/1000 ]; 132 self.showView.text = speedFeedback; 133 } 135 self.prevLocation = newLocation; 136 } 137 } 138 139 @end |
上面程序中的两行红色字代码分贝用于计算本次定位数据与上次定位数据之间的时间差、距离,用此距离除以时间,即可得到该设备的当前速度。该程序还定义了一个sumDistance 属性来保存设备移动的总距离,并定义一个sumTime来保存设备移动的总时间,用设备移动的总距离除以设备移动的总时间,即可获取该设备移动的平均速度。 |
方向监测
iOS系统通过heading属性来获取设备的真实方向。需要指出的是,并不是所有的iOS设备都支持heading属性,因此程序获取
方向之前需要先测试该设备是否支持heading。如果定位管理器支持heading属性,那么CLLocationManager的headingAvailable属性
将会返回“YES”。
使用CLLocationManager获取设备方向与获取移动距离的步骤基本相似,只是此时不是检测位置移动,而是检测方向改变。
使用CLLocationManager获取设备方向的步骤如下: |
1.创建CLLocationManager对象,该对象负责获取定位相关信息。并为该对象设置一些必要的属性。 |
2.为CLLocationManager指定delegate属性,该属性值必须是一个实现CLLocationManagerDelegate协议的对象。实现CLLocationManagerDelegate协议时可根据需要实现协议中特定的方法。 |
3.调用CLLocationManager的startUpdatingHeading方法获取方向信息。获取方向结束时,可调用stopUpdatingHeading方法结束获取方向信息。 |
当设备的方向改变时,iOS系统将会自动激发CLLocationManager的delegate对象的locationManager:didUpdateHeading:方法,而程序可通过重写该方法来获取设备方向。 |
iOS允许为检测方向改变设置如下属性。 |
CLLocationDegrees headingFilter:设置只有当设备方向的改变值超过该属性值时才激发delegate的方法。 |
CLDeviceOrientation headingOrientation:设置设备当前方向。 |
监听方向时返回的是一个CLHeading对象,该对象包含如下属性。 |
magneticHeading:该属性返回设备与磁北的相对方向。 |
trueHeading:该属性返回值设备与真北的相对方向。 真北始终指向地理北极点;磁北则对应于随时间变化的地球磁场北极。iOS系统使用一个计算后的偏移量(称为偏差)来确定这两者之间的差异。 |
headingAccuracy:该属性返回方向值的误差范围。 |
timestamp:该属性返回方向值的生成时间。 |
x:获取该设备在x方向上监听得到的原始磁力值,该磁力值的强度单位是微特斯拉 |
y: 获取该设备在Y方向上监听得到的原始磁力值,该磁力值的强度单位是微特斯拉。 |
z:获取该设备在Z方向上监听得到的原始磁力值,该磁力值的强度单位是微特斯拉。 |
代码片段:指南针 |
1 ViewController.m 2 3 @interface ViewController () <CLLocationManagerDelegate> 5 { 7 CALayer* znzLayer; 9 } 11 @property (nonatomic, strong)CLLocationManager* locationManager; 13 @end 15 @implementation ViewController 17 - (void)viewDidLoad 18 19 { 21 [super viewDidLoad]; 22 23 // 如果磁力计可用,则开始监听方向改变 25 if([CLLocationManager headingAvailable]) 27 { 28 29 // 创建显示方向的指南针图片 Layer 30 31 znzLayer = [[CALayer alloc] init]; 32 33 NSInteger screenHeight = [UIScreen mainScreen].bounds.size.height; 34 35 NSInteger y = (screenHeight - 320) / 2; 36 37 znzLayer.frame = CGRectMake(0, y, 320, 320); 38 39 // 设置znzLayer显示的图片 40 41 znzLayer.contents = (id)[[UIImage imageNamed:@”znz.png”] CGImage]; 42 43 // 将znzLayer添加到系统的UIView中 44 45 [self.view.layer addSublayer:znzLayer]; 46 47 // 创建CLLocationManager对象 48 49 self.locationManager = [[CLLocationManager alloc] init]; 50 51 self.locationManager.delegate = self; 52 53 [self.locationManager startUpdatingHeading]; 54 55 } 56 57 // 如果磁力计不可用,则使用UIAlertView显示提示信息 58 59 else 60 61 { 62 63 // 使用警告框提醒用户 64 65 [[[UIAlertView alloc] initWithTitle:@” 提 醒 ” 66 67 message:@” 您的设备不支持磁力计 ” delegate:self 68 69 cancelButtonTitle:@” 确定 ” otherButtonTitles:nil ] 70 71 show ]; 72 73 } 74 75 } 76 77 // 当成功获取设备的方向值后激发该方法 78 79 - (void)locationManager: (CLLocationManager*)manager 80 81 didUpdateHeading: (CLHeading*) newHeading 82 83 { 84 85 // 将设备的方向角度换算成弧度 86 87 CGFloat headings = -1.0f * M_PI * newHeading.magneticHeading / 180.0f; 88 89 // 创建不断改变CALayer的transform属性的属性动画 90 91 CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@”transform”]; 92 93 CATransform3D fromValue = znzLayer.transform; 94 95 // 设置动画开始的属性值 96 97 anim.formValue = [NSValue valueWithCATransform3D: fromValue]; 98 99 // 绕z轴旋转heading弧度的变换矩阵 100 101 CATransform3D toValue = CATransform3DMakeRotation(headings, 0 , 0, 1); 102 103 // 设置动画结束的属性 104 105 anim.toValue = [NSValue valueWithCATransform3D: toValue]; 106 107 anim.duration = 0.5; 108 109 anim.removedOnCompletion = YES; 110 111 // 设置动画结束后znzLayer的变换矩阵 112 113 znzLayer.transform = toValue; 114 115 // 为znzLayer添加动画 116 117 [znzLayer addAnimation:anim forKey:nil]; 118 119 } 120 121 - (BOOL)locationManagerShouldDisplayHeadingCalibration: (CLLocationManager*) 122 123 manager 124 125 { 127 return YES; 128 129 } 130 131 @end |
上面程序中的前两行红色字代码用于为CLLocationManager设置delegate属性,接下来程序调用该对象的startUpdatingHeading方法开始监听设备的方向改变-------当设备的方向改变时,系统会自动激发CLLocationManager设置delegate的locationManager:didUpdateHeading:方法,程序的视图控制器重写了该方法,并在该方法中获取设备方向,然后将图片“反转”相应的角度,从而让图片的北极总是指向真实的北极。 |
区域监测
如果希望iOS设备进入某个区域发出通知,那么这种区域检测的工能被称为临近警告。
用户设备不断地临近指定固定点,当与该固定点的距离小于指定范围时,系统可以触发相应的处理。用户设备离开指定固定点,
当与该固定点的距离大于指定范围时,系统也可以触发相应的处理。
iOS的区域监测同样可以使用CLLocationManager来实现,监听设备是否进入、离开某个区域的步骤如下。 |
|
当设备进入指定区域时,iOS系统将会自动激发CLLocationManager的delegate对象的locationManager:didEnterRegion:方法;当设备离开指定区域时,iOS系统将会自动激发CLLocationManager的delegate对象的locationManager:didExitRegion:方法,重写这两个方法对用户进行提醒。 |
iOS提供了CLRegion来定义被 监测的区域,但实际编程中推荐使用CLCircularRegion(CLRegion的子类)创建圆形区域,创建CLCircularRegion对象时无非就是指定圆心、半径等信息,非常简单。 |
代 码 片 段 |
1 ViewController.m 2 @interface ViewController ()<CLLocationManagerDelegate> 3 @property (retain, nonatomic) CLLocationManager * locationManager; 4 @end 5 6 @implementation ViewController 7 8 - (void)viewDidLoad 9 { 11 [super viewDidLoad]; 13 if([CLLocationManager locationServiceEnabled]) 15 { 17 self.locationManager = [[CLLocationManager alloc] init]; 19 self.locationManager.delegate = self; 21 // 定义一个CLLocationCoordinate2D作为区域的圆心 23 CLLocationCoordinate2D companyCenter; 25 companyCenter.latitude = 23.126272; 27 companyCenter.longitude = 123.398568; 29 // 使用CLCircularRegion创建一个圆形区域,半径为500米 30 CLRegion* shanghai = [[CLCircularRegion alloc] initWithCenter: companyCenter 31 radius: 500 Identifier:@”shanghai”]; 32 // 开始监听shanghai区域 33 [self.locationManager startMonitoringForRegion: shanghai]; 35 } 37 else 39 { 41 // 使用警告框提醒用户 42 43 [[[UIAlertView alloc] initWithTitle:@” 提 醒 ” 44 message:@”您的设备不支持定位” delegate:self 45 cancelButtonTitle:@”确 定” otherButtonTitles:nil] show]; 47 } 49 } 51 // 进入指定区域以后将弹出提示框提示用户 52 53 - (void)locationManager: (CLLocationManager*)manager 54 didEnterRegion: (CLRegion*)region 55 { 57 [[[UIAlertView alloc] initWithTitle:@”区域检测提示” 59 message:@” 您已经【进入】上海! ” delegate:nil 61 cancelButtonTitle:@”OK” otherButtonTitles:nil] 63 show]; 65 } 66 67 // 离开指定区域以后将弹出提示框提示用户 68 69 - (void)locationManager: (CLLocationManager *)manager 70 didExitRegion: (CLRegion*)region 71 { 72 73 [[[UIAlertView alloc] initWithTitle:@”区域检测提示” 75 message:@” 您已经【离开】上海! ” delegate:nil 77 cancelButtonTitle:@”OK” otherButtonTitles:nil] 79 show]; 80 81 } 82 83 @end |
正如上面程序中的红色字代码所看到的,第1行红色字代码创建了一个CLRegion对象作为被监测区域,接下来调用CLLocationManager的startMonitoringForRegion:方法监听该设备是否进入/离开指定区域。 当设备进入或离开中区域时,iOS系统都会自动激发CLLocationManager的delegate对象的相应方法,上面程序重写了这两个方法对用户进行提醒。 |