Core Location

Core Location是iOS SDK中一个提供设备位置的框架。可以使用三种技术来获取位置:GPS、蜂窝或WiFi。在这些技术中,GPS最为精准,如果有GPS硬件,Core Location将优先使用它。如果设备没有GPS硬件(如WiFi iPad)或使用GPS获取当前位置时失败,Core Location将退而求其次,选择使用蜂窝或WiFi。

Core Location的大多数功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器来指定位置更新的频率和精度,以及开始和停止接收这些更新。

要使用位置管理器,必须首先将框架Core Location加入到项目中,再导入其接口文件:

?
1
# import <corelocation corelocation.h= "" ></corelocation>
接下来,需要分配并初始化一个位置管理器实例、指定将接收位置更新的委托并启动更新:
?
1
2
3
4
CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[locManager startUpdatingLocation];
//[locManager stopUpdatingLocation];
位置管理器委托(CLLocationManagerDelegate)有两个与位置相关的方法:
?
1
2
3
4
5
6
7
8
9
- ( void )locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
     
}
 
- ( void )locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
     
}

第一个方法处理定位成功,manager参数表示位置管理器实例;locations为一个数组,是位置变化的集合,它按照时间变化的顺序存放。如果想获得设备的当前位置,只需要访问数组的最后一个元素即可。集合中每个对象类型是CLLocation,它包含以下属性:

coordinate — 坐标。一个封装了经度和纬度的结构体。

altitude — 海拔高度。正数表示在海平面之上,而负数表示在海平面之下。

horizontalAccuracy — 位置的精度(半径)。位置精度通过一个圆表示,实际位置可能位于这个圆内的任何地方。这个圆是由coordinate(坐标)和horizontalAccuracy(半径)共同决定的,horizontalAccuracy的值越大,那么定义的圆就越大,因此位置精度就越低。如果horizontalAccuracy的值为负,则表明coordinate的值无效。

verticalAccuracy — 海拔高度的精度。为正值表示海拔高度的误差为对应的米数;为负表示altitude(海拔高度)的值无效。

speed — 速度。该属性是通过比较当前位置和前一个位置,并比较它们之间的时间差异和距离计算得到的。鉴于Core Location更新的频率,speed属性的值不是非常精确,除非移动速度变化很小

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- ( void )locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
     CLLocation *curLocation = [locations lastObject];
     
     if (curLocation.horizontalAccuracy > 0 )
     {
         NSLog(@ "当前位置:%.0f,%.0f +/- %.0f meters" ,curLocation.coordinate.longitude,
               curLocation.coordinate.latitude,
               curLocation.horizontalAccuracy);
     }
     
     if (curLocation.verticalAccuracy > 0 )
     {
         NSLog(@ "当前海拔高度:%.0f +/- %.0f meters" ,curLocation.altitude,curLocation.verticalAccuracy);
     }
}

应用程序开始跟踪用户的位置时,将在屏幕上显示一个是否允许定位的提示框。如果用户禁用定位服务,iOS不会禁止应用程序运行,但位置管理器将生成错误。

第二个方法处理这种定位失败,该方法的参数指出了失败的原因。如果用户禁止应用程序定位,error参数将为kCLErrorDenied;如果Core Location经过努力后无法确认位置,error参数将为kCLErrorLocationUnknown;如果没有可供获取位置的源,error参数将为kCLErrorNetwork。

通常,Core Location将在发生错误后继续尝试确定位置,但如果是用户禁止定位,它就不会这样做;在这种情况下,应使用方法stopUpdatingLocation停止位置管理器。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- ( void )locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
     if (error.code == kCLErrorLocationUnknown)
     {
         NSLog(@ "Currently unable to retrieve location." );
     }
     else if (error.code == kCLErrorNetwork)
     {
         NSLog(@ "Network used to retrieve location is unavailable." );
     }
     else if (error.code == kCLErrorDenied)
     {
         NSLog(@ "Permission to retrieve location is denied." );
         [manager stopUpdatingLocation];
     }
}
可根据实际情况来指定位置精度。例如,对于只需确定用户在哪个国家的应用程序,没有必要要求Core Location的精度为10米。要指定精度,可在启动位置更新前设置位置管理器的desiredAccuracy。有6个表示不同精度的枚举值
?
1
2
3
4
5
6
extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation;
extern const CLLocationAccuracy kCLLocationAccuracyBest;
extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;
extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;
extern const CLLocationAccuracy kCLLocationAccuracyKilometer;
extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;
对位置管理器启动更新后,更新将不断传递给位置管理器委托,直到停止更新。您无法直接控制这些更新的频率,但可使用位置管理器的属性distanceFilter进行间接控制。在启动更新前设置属性distanceFilter,它指定设备(水平或垂直)移动多少米后才将另一个更新发送给委托。下面的代码使用适合跟踪长途跋涉者的设置启动位置管理器:
?
1
2
3
4
5
CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
locManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
locManager.distanceFilter = 200 ;
[locManager startUpdatingLocation];

P.s. 定位要求的精度越高、属性distanceFilter的值越小,应用程序的耗电量就越大。

位置管理器有一个headingAvailable属性,它指出设备是否装备了磁性指南针。如果该属性为YES,就可以使用Core Location来获取航向(heading)信息。接收航向更新与接收位置更新极其相似,要开始接收航向更新,可指定位置管理器委托,设置属性headingFilter以指定要以什么样的频率(以航向变化的度数度量)接收更新,并对位置管理器调用方法startUpdatingHeading:

位置管理器委托协议定义了用于接收航向更新的方法。该协议有两个与航向相关的方法:

?
1
2
3
4
5
6
7
8
9
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
     return YES;
}
 
- ( void )locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
     
}

第一个方法指定位置管理器是否向用户显示校准提示。该提示将自动旋转设备360°。由于指南针总是自我校准,因此这种提示仅在指南针读数剧烈波动时才有帮助。当设置为YES后,提示可能会分散用户的注意力,或影响用户的当前操作。

第二个方法的参数newHeading是一个CLHeading对象。CLHeading通过一组属性来提供航向读数:magneticHeading和trueHeading。这些值的单位为度,类型为CLLocationDirection,即双精度浮点数。这意味着:

如果航向为0.0,则前进方向为北;

如果航向为90.0,则前进方向为东;

如果航向为180.0,则前进方向为南;

如果航向为270.0,则前进方向为西。

CLHeading对象还包含属性headingAccuracy(精度)、timestamp(读数的测量时间)和description(这种描述更适合写入日志而不是显示给用户)。下面演示了利用这个方法处理航向更新:

?
1
2
3
4
5
6
7
8
9
- ( void )locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
     if (newHeading.headingAccuracy >= 0 )
     {
         NSString *headingDesc = [NSString stringWithFormat:@ "%.0f degrees (true), %.0f degrees (magnetic)" ,newHeading.trueHeading,newHeading.magneticHeading];
         
         NSLog(@ "%@" ,headingDesc);
     }
}

trueHeading和magneticHeading分别表示真实航向和磁性航向。如果位置服务被关闭了,GPS和wifi就只能获取magneticHeading(磁场航向)。只有打开位置服务,才能获取trueHeading(真实航向)。

下面的代码演示了,当存在一个确定了经纬度的地点,当前位置离这个地点的距离及正确航向:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# import "ViewController.h"
 
#define kDestLongitude 113.12 //精度
#define kDestLatitude 22.23 //纬度
#define kRad2Deg 57.2957795 // 180/π
#define kDeg2Rad 0.0174532925 // π/180
 
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UILabel *lblMessage;
@property (strong, nonatomic) IBOutlet UIImageView *imgView;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLLocation *recentLocation;
 
-( double )headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current;
@end
 
@implementation ViewController
 
- ( void )viewDidLoad
{
     [ super viewDidLoad];
 
     self.locationManager = [[CLLocationManager alloc] init];
     self.locationManager.delegate = self;
     self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
     self.locationManager.distanceFilter = 1609 ; //1英里≈1609米
     [self.locationManager startUpdatingLocation];
     
     if ([CLLocationManager headingAvailable])
     {
         self.locationManager.headingFilter = 10 ; //10°
         [self.locationManager startUpdatingHeading];
     }
}
 
/*
  * According to Movable Type Scripts
  *
  *  Javascript:
  *
  * var y = Math.sin(dLon) * Math.cos(lat2);
  * var x = Math.cos(lat1)*Math.sin(lat2) -
  * Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
  * var brng = Math.atan2(y, x).toDeg();
  */
-( double )headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current
{
     double lat1 = current.latitude*kDeg2Rad;
     double lat2 = desired.latitude*kDeg2Rad;
     double lon1 = current.longitude;
     double lon2 = desired.longitude;
     double dlon = (lon2-lon1)*kDeg2Rad;
     
     double y = sin(dlon)*cos(lat2);
     double x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(dlon);
     
     double heading=atan2(y,x);
     heading=heading*kRad2Deg;
     heading=heading+ 360.0 ;
     heading=fmod(heading, 360.0 );
     return heading;
}
 
//处理航向
- ( void )locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
     if (self.recentLocation!=nil && newHeading.headingAccuracy>= 0 )
     {
         CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
         
         double course = [self headingToLocation:destLocation.coordinate current:self.recentLocation.coordinate];
         
         double delta = newHeading.trueHeading - course;
         
         if (abs(delta) <= 10 )
         {
             self.imgView.image = [UIImage imageNamed:@ "up_arrow.png" ];
         }
         else
         {
             if (delta > 180 )
             {
                 self.imgView.image = [UIImage imageNamed:@ "right_arrow.png" ];
             }
             else if (delta > 0 )
             {
                 self.imgView.image = [UIImage imageNamed:@ "left_arrow.png" ];
             }
             else if (delta > - 180 )
             {
                 self.imgView.image = [UIImage imageNamed:@ "right_arrow.png" ];
             }
             else
             {
                 self.imgView.image = [UIImage imageNamed:@ "left_arrow.png" ];
             }
         }
         self.imgView.hidden = NO;
     }
     else
     {
         self.imgView.hidden = YES;
     }
}
 
//处理定位成功
- ( void )locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
     CLLocation *curLocation = [locations lastObject];
     
     if (curLocation.horizontalAccuracy >= 0 )
     {
         self.recentLocation = curLocation;
         
         CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
         
         CLLocationDistance distance = [destLocation distanceFromLocation:curLocation];
         
         if (distance< 500 )
         {
             [self.locationManager stopUpdatingLocation];
             [self.locationManager stopUpdatingHeading];
             self.lblMessage.text = @ "您已经到达目的地!" ;
         }
         else
         {
             self.lblMessage.text = [NSString stringWithFormat:@ "距离目的地还有%f米" ,distance];
         }
     }
}
 
//处理定位失败
- ( void )locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
     if (error.code == kCLErrorLocationUnknown)
     {
         NSLog(@ "Currently unable to retrieve location." );
     }
     else if (error.code == kCLErrorNetwork)
     {
         NSLog(@ "Network used to retrieve location is unavailable." );
     }
     else if (error.code == kCLErrorDenied)
     {
         NSLog(@ "Permission to retrieve location is denied." );
         [self.locationManager stopUpdatingLocation];
         self.locationManager = nil;
     }
}
 
 
- ( void )didReceiveMemoryWarning
{
     [ super didReceiveMemoryWarning];
     // Dispose of any resources that can be recreated.
}
 
@end





地图显示

最简单显示地图的代码:

  RootViewController

注意:使用地图之前是需要引入MapKit框架的哦.

autoresizingMask是干什么用的呢(实际上,我看过后还是不懂)?

UIViewAutoresizingNone

这个常量如果被设置,视图将不进行自动尺寸调整。

UIViewAutoresizingFlexibleHeight

这个常量如果被设置,视图的高度将和父视图的高度一起成比例变化。否则,视图的高度将保持不变。

UIViewAutoresizingFlexibleWidth

这个常量如果被设置,视图的宽度将和父视图的宽度一起成比例变化。否则,视图的宽度将保持不变。

UIViewAutoresizingFlexibleLeftMargin

这个常量如果被设置,视图的左边界将随着父视图宽度的变化而按比例进行调整。否则,视图和其父视图的左边界的相对位置将保持不变。

UIViewAutoresizingFlexibleRightMargin

这个常量如果被设置,视图的右边界将随着父视图宽度的变化而按比例进行调整。否则,视图和其父视图的右边界的相对位置将保持不变。

UIViewAutoresizingFlexibleBottomMargin

这个常量如果被设置,视图的底边界将随着父视图高度的变化而按比例进行调整。否则,视图和其父视图的底边界的相对位置将保持不变。

UIViewAutoresizingFlexibleTopMargin

这个常量如果被设置,视图的上边界将随着父视图高度的变化而按比例进行调整。否则,视图和其父视图的上边界的相对位置将保持不变。

  RootViewController.m

 

 

位置定位

  RootViewController.m

为什么需要判断定位功能可不可用呢?下图可以看出为什么了.

打印信息:

2014-05-15 09:25:11.883 CoreLocation[17610:60b] <+37.78583400,-122.40641700> +/- 5.00m (speed -1.00 mps / course -1.00) @ 5/15/14, 9:25:11 AM China Standard Time

本人将这个代理定位的方式改写成了可以使用block的方式:

YXLocation.h

  Code

YXLocation.m

  Code

 

 

将经纬度转换为有意义的地址

  RootViewController.m

打印信息如下:

2014-05-15 09:40:13.982 CoreLocation[2482:60b] 我在 中国北京市东城区东四街道东四朝阳门北小街2-1号
2014-05-15 09:40:13.986 CoreLocation[2482:60b] 国家代码 CN
2014-05-15 09:40:13.987 CoreLocation[2482:60b] 国家    中国
2014-05-15 09:40:13.988 CoreLocation[2482:60b] 邮政编码 (null)
2014-05-15 09:40:13.989 CoreLocation[2482:60b] administrativeArea 北京市
2014-05-15 09:40:13.991 CoreLocation[2482:60b] locality (null)
2014-05-15 09:40:13.992 CoreLocation[2482:60b] subLocality 东城区
2014-05-15 09:40:13.993 CoreLocation[2482:60b] subThoroughfare 2-1号

 

 

将有意义的地址转换为经纬度

  RootViewController.m

打印信息:

2014-05-15 09:51:15.270 CoreLocation[2525:60b] 发现了 2 placemark(s).
2014-05-15 09:51:15.274 CoreLocation[2525:60b] 经度 = 116.425960
2014-05-15 09:51:15.275 CoreLocation[2525:60b] 纬度 = 39.931609

 

 

直接显示用户当前位置

  RootViewController.m

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值