文档章节

iPhone实战:定位与地图

vimfung
 vimfung
发布于 2012/07/07 00:18
字数 2007
阅读 4244
收藏 9

       个人觉得地理位置这部分的知识其实是相当有趣的。说到定位其实在大多数的社交软件中都有这样的一个功能,用户可以共享自己的位置并且查看其他用户的位置,从而更容易地结交一些附近或者在同一地区的朋友,又或者在地图上显示自己的当前位置,并且显示附近的餐厅或者咖啡厅。这些功能看起来是挺复杂,但在实际的开发过程中,iOS为我们封装了大部分的功能,让我们不需要学习太底层的知识,通过他提供的两个库就能轻松实现。接下来我会一步一步地讲述相关知识。

1、获取当前定位

      iOS提供了一个叫作CoreLocation.framework的框架。使用他可以取到自己的定位信息(经纬度)。请参考下面代码片段:

 

if([CLLocationManager locationServicesEnabled]){
        //定位功能开启的情况下进行定位
        CLLocationManager *manager = [[CLLocationManager alloc] init];
        manager.distanceFilter = kCLDistanceFilterNone;
        manager.desiredAccuracy = kCLLocationAccuracyBest;
        manager.delegate = self;
        [manager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation
{
    
    [manager stopUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager 
       didFailWithError:(NSError *)error
{
    [manager stopUpdatingLocation];
}

 

       如上面代码所示CLLocationManager就是用于获取定位信息对象类,在实际应用中可以根据自己的需要来设置定位的更新频率以及定位准确度。其中代码中的distanceFilter表示更新位置的距离,假如超过设定值则进行定位更新,否则不更新。代码中的kCLDistanceFilterNone表示不设置距离过滤,即随时更新地理位置。desiredAccuracy属性表示取得定位的精度,kCLLocationAccuracyBest表示最精确,但也预示着需要消耗更多的时间和电量,所以应该根据需要设定。

       那么CLLocationManager是通过什么方法来开启定位的呢?他是通过调用startUpdatingLocation开启定位功能,然后使用stopUpdatingLocation停止定位,其中定位信息是通过loctionManager:didUpdateToLocation:fromLocation;委托方法来通知委托对象的,因此委托对象必须实现CLLocationManagerDelegate委托。在返回定位信息委托方法中主要的两个参数是newLocationoldLocationnewLocation表示最新定位,oldLocation表示上一次的定位信息。这两个都是CLLocation对象。以下是CLLocation的属性说明:

属性

描述

altitude

海拔高度

coordinate

经纬度

course

行驶方向

horizontalAccuracy

水平方向的精确度

Speed

行驶速度

timestamp

时间戳

verticalAccuracy

垂直方向的精确度

 

2、获取地理位置信息

    当你取到了一个经纬度信息时,也许还有这样的一个需求,那就是当前的经纬度所对应的地理位置信息是什么。那么这时候我们需要用到框架来为我们实现这一功能,那就是MapKit.framework。在这个框架中有一个叫MKReverseGeocoder的类可以帮助我们实现反向解析地理位置。请看一下代码:

 

-  (void)locationManager:(CLLocationManager *)manager 
     didUpdateToLocation:(CLLocation *)newLocation 
            fromLocation:(CLLocation *)oldLocation
{
    MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];     
    geocoder.delegate = self;     
    [geocoder start];
}

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder 
       didFindPlacemark:(MKPlacemark *)placemark
{
     NSLog(@"\n country:%@\n postalCode:%@\n ISOcountryCode:%@\n locality:%@\n subLocality:%@\n administrativeArea:%@\n subAdministrativeArea:%@\n thoroughfare:%@\n subThoroughfare:%@\n",
           placemark.country,           
           placemark.postalCode,           
           placemark.ISOcountryCode,           
           placemark.administrativeArea,            
           placemark.subAdministrativeArea,           
           placemark.locality,           
           placemark.subLocality,           
           placemark.thoroughfare,
           placemark.subThoroughfare); 
}  

-  (void)reverseGeocoder:(MKReverseGeocoder *)geocoder 
        didFailWithError:(NSError *)error
{     
    NSLog(@"reverse geocoder fail!!"); 
}

 

上面的代码是在获取到经纬度后,立刻进行反向地理位置解析的。其实MKReverseGeocoder用法也比较简单,通过经纬度初始化后直接调用start方法就可以实现反向解析了,然后等待返回,其返回是通过委托形式通知的。所以委托对象必须实现MKReverseGeocoderDelegate委托。解析成功后会返回一个MKPlacemark的对象里面包含了相关的地理位置信息(包括国家、地区、街道等)。

    但从iOS5之后MKReverseGeocoder成为了不推荐使用的类。因此有一个新的类取代了他的作用,那就是CLGeocoder类,使用该类进行反向解析也非常容易,请看下面代码:

 

CLGeocoder *geocoder=[[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:newLocation                     
               completionHandler:^(NSArray *placemarks,
                                   NSError *error)
               {                        
                       CLPlacemark *placemark=[placemarks objectAtIndex:0];            
                       NSLog(@"name:%@\n country:%@\n postalCode:%@\n ISOcountryCode:%@\n ocean:%@\n inlandWater:%@\n locality:%@\n subLocality:%@\n administrativeArea:%@\n subAdministrativeArea:%@\n thoroughfare:%@\n subThoroughfare:%@\n",
                              placemark.name,
                              placemark.country,
                              placemark.postalCode,
                              placemark.ISOcountryCode,
                              placemark.ocean,
                              placemark.inlandWater,
                              placemark.administrativeArea,
                              placemark.subAdministrativeArea,
                              placemark.locality,
                              placemark.subLocality,
                              placemark.thoroughfare,
                              placemark.subThoroughfare);
              }];

 

从代码来看,CLGeocoder类没有使用委托的形式通知返回状态,而是通过block的方式进行回调,而且MKReverseGeocoder委托只返回了一个地标位置,但是CLGeocoder则返回了一个包含多个地标位置的数组,但这个数组在通常状态下是只有一个元素,如果存在多个元素那证明了给定解析的经纬度被解析到多个不同的地标信息。如果解析错误或者调用cancel方法则此参数为nil

3、地图显示

    想更加形象地表现出位置信息靠文字的描述是远远不够的,因为使用地图来显示地理位置将会给用户带来全新的体验。在iOS里面已经将Google地图封装到SDK里面了,我们可以用很少的代码来实现很多在地图上的操作(如标记位置、绘画线路等)。下面的代码是生成一张地图并显示到界面上:

 

-  (void)viewDidLoad
{
	[super viewDidLoad];
        MKMapView *mapView=[[MKMapView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460.0)]; 	  
        mapView.delegate = self;
        [self.view addSubview:mapView];
        [mapView release];
}

 

    这够简单吧,上面的设置地图委托对象是因为在下面要标记地理位置时需要用到的。那么如何把取到的经纬度信息显示到地图上呢?其实每个坐标信息在地图中显示后都对应一个MKAnnotationView,而MKAnnotationView又负责解析了一个实现MKAnnotation协议的数据对象。因此我们首先要做的事情就是把取到的经纬度转换为MKAnnotation协议对象。先定义一个实现MKAnnotation协议的类:

 

@interface  DemoAnnotation : NSObject<MKAnnotation> {
	CLLocationCoordinate2D _coordinate;
}

-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate;

@end 

@implementation DemoAnnotation

@synthesize coordinate=_coordinate;

-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate{
	if (self = [super init]) {
		_coordinate=coordinate;
	}
	return self;
}

-(void)setCoordinate:(CLLocationCoordinate2D)newCoordinate{
	_coordinate=newCoordinate;
}

-(NSString *)title{
	return @”我的位置”;
}

-(NSString *)subtitle{
	return nil;
}

@end

 

上面的类只是简单地保存了经纬度信息。记得注意的是MKAnnotation协议中的titlesubtitle的作用,如果你在显示AnnotationView设置其canShowCallout属性为YES时,则当用户点击AnnotationView时会弹出一个Callout视图,用于显示titlesubtitle,假如设置titlenil那么即使canShowCalloutYES也不会弹出Callout视图。

接下来要改写一下获取定位成功后的方法,等待获取定位成功后把经纬度设置到地图上显示。实现代码如下:

 

-  (void)locationManager:(CLLocationManager *)manager 
     didUpdateToLocation:(CLLocation *)newLocation 
            fromLocation:(CLLocation *)oldLocation
{
	DemoAnnotation *annotation = [[DemoAnnotation alloc] initWithCoordinate:newLocation.coordinate];
	[_mapView addAnnotation:annotation];
	[annotation release];

	MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];     
        geocoder.delegate = self;     
        [geocoder start];
}

 

上述代码中加粗部分的代码就是把经纬度信息封装到刚才定义好的对象中。然后通过addAnnotation方法传递给MapView;这里的_mapView是把之前viewDidLoad方法中的临时变量改变为类属性以达到跨方法引用的目的。经过上面步骤已经把我的位置引入到地图里面了,但现在还不会显示在地图上,因为还需要实现MapView中的协议,告诉MapView如何显示你Annotation。以下代码采用iOS中默认的大头针样式来显示位置。如下:

 

-  (MKAnnotationView *)mapView:(MKMapView *)mapView 
             viewForAnnotation:(id <MKAnnotation>)annotation
{
    NSString *annotationViewId=@"CurrentUserAnnotationView";
    MKPinAnnotationView *annotationView = (MKPinAnnotationView *)
    [mapView dequeueReusableAnnotationViewWithIdentifier:annotationViewId];
    if (annotationView==nil)
    {
        annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationViewId] autorelease];
	annotationView.canShowCallout = YES;
    }
    return annotationView;
}

 

上面的协议方法就是MapView告诉你他需要显示哪个annotation,然后显示的样式由用户你自己决定,但必须要继承MKAnnotationView类。

到这里对于定位和地图的应用就告一段落了,当然关于 MKMapView 还有一些更加高级的特性,例如:动态编辑 Annotation 、绘画路线图等应用我在这里暂时不说了,等偶再研究透彻一点再给大家分享。

 

 

 

 

 

 

 

© 著作权归作者所有

vimfung

vimfung

粉丝 60
博文 59
码字总数 85338
作品 4
广州
技术主管
私信 提问
加载中

评论(12)

总是搞不懂
总是搞不懂
原来是这样 这下明白了 受教了! 非常感谢!
vimfung
vimfung

引用来自“BUJIBUJI可”的评论

引用来自“vimfung”的评论

引用来自“BUJIBUJI可”的评论

兄弟 我晚上重新试了下 换了台iPhone 然后可以了 可能时我之前那台Touch越狱了还是什么原因导致有问题 搞了两天 问题竟然出在设备上而不是软件上 很感谢你啦 虽然问题不是在软件上 热心帮我找问题 非常感谢!

Touch没有GPS模块,无法在室外定位,只能根据wifi来定位,

我在室外测试也是有接WIFI的 可是还是不行 呵呵 不懂事不是因为我越狱出的问题

wifi的一般都是固定的位置的,不容易变更位置。除非在测试过程中每到一个地方就更换连接的wifi,否则位置的变化过程不明显。如果只是通过手机做wifi热点连接的话那更有可能是无法定位,因为热点的wifi是没使用过的,根本没有位置信息可以查询。
总是搞不懂
总是搞不懂

引用来自“vimfung”的评论

引用来自“BUJIBUJI可”的评论

兄弟 我晚上重新试了下 换了台iPhone 然后可以了 可能时我之前那台Touch越狱了还是什么原因导致有问题 搞了两天 问题竟然出在设备上而不是软件上 很感谢你啦 虽然问题不是在软件上 热心帮我找问题 非常感谢!

Touch没有GPS模块,无法在室外定位,只能根据wifi来定位,

我在室外测试也是有接WIFI的 可是还是不行 呵呵 不懂事不是因为我越狱出的问题
vimfung
vimfung

引用来自“BUJIBUJI可”的评论

兄弟 我晚上重新试了下 换了台iPhone 然后可以了 可能时我之前那台Touch越狱了还是什么原因导致有问题 搞了两天 问题竟然出在设备上而不是软件上 很感谢你啦 虽然问题不是在软件上 热心帮我找问题 非常感谢!

Touch没有GPS模块,无法在室外定位,只能根据wifi来定位,
总是搞不懂
总是搞不懂
兄弟 我晚上重新试了下 换了台iPhone 然后可以了 可能时我之前那台Touch越狱了还是什么原因导致有问题 搞了两天 问题竟然出在设备上而不是软件上 很感谢你啦 虽然问题不是在软件上 热心帮我找问题 非常感谢!
vimfung
vimfung

引用来自“BUJIBUJI可”的评论

引用来自“vimfung”的评论

引用来自“BUJIBUJI可”的评论

兄弟 请教个问题:GPS定位的时候遇到 startUpdatingLocation之后didUpdateToLocation 只调用了几次 然后就不调用了 我需要一直更新位置 模拟器上没问题 真机上就出现这种情况 知道怎么搞吗

我觉得奇怪,你的模拟器上怎么能测试定位呢?
另外更新位置只调用了几次,如果你不是运动的,那调用了几次然后不调用了那是正常的,因为你的位置没有变更。如果你是运动的,那请检查distanceFilter是否设置了一个阀值,因为只有超过阀值才会在次调用。

模拟器定位不了 但是断点可以看到有触发didUpdateToLocation 每秒触发一次 distanceFilter我没有进行设置 不知道默认的阈值是多少米

你用MapKit定位?还是CLLocationManager?如果是CLLocationManger请设置distanceFilter看一下。
总是搞不懂
总是搞不懂
我在运动中监视了didUpdateToLocation 这个方法 就是刚打开Mapview在的那个View的时候看到有触发一次或者两次 之后就没触发了 哎 郁闷死呀
总是搞不懂
总是搞不懂

引用来自“vimfung”的评论

引用来自“BUJIBUJI可”的评论

兄弟 请教个问题:GPS定位的时候遇到 startUpdatingLocation之后didUpdateToLocation 只调用了几次 然后就不调用了 我需要一直更新位置 模拟器上没问题 真机上就出现这种情况 知道怎么搞吗

我觉得奇怪,你的模拟器上怎么能测试定位呢?
另外更新位置只调用了几次,如果你不是运动的,那调用了几次然后不调用了那是正常的,因为你的位置没有变更。如果你是运动的,那请检查distanceFilter是否设置了一个阀值,因为只有超过阀值才会在次调用。

模拟器定位不了 但是断点可以看到有触发didUpdateToLocation 每秒触发一次 distanceFilter我没有进行设置 不知道默认的阈值是多少米
vimfung
vimfung

引用来自“BUJIBUJI可”的评论

兄弟 请教个问题:GPS定位的时候遇到 startUpdatingLocation之后didUpdateToLocation 只调用了几次 然后就不调用了 我需要一直更新位置 模拟器上没问题 真机上就出现这种情况 知道怎么搞吗

我觉得奇怪,你的模拟器上怎么能测试定位呢?
另外更新位置只调用了几次,如果你不是运动的,那调用了几次然后不调用了那是正常的,因为你的位置没有变更。如果你是运动的,那请检查distanceFilter是否设置了一个阀值,因为只有超过阀值才会在次调用。
总是搞不懂
总是搞不懂
兄弟 麻烦你了 如果愿意帮忙看下 请加我QQ371028014 不胜感激
查找我的iphone怎么用详细教程

很多人都知道在苹果的Ios 6系统之前有一个叫查找我的iphone的app,英文名称是Find My Iphone,用户如果需要打开查找我的iphone的功能需要单独下载这款app。iOS 7之后,苹果对“查找我的iPhon...

TheBeauty2016
2017/05/11
0
0
HTML5 Geolocation(地理定位)

HTML5 Geolocation(地理定位)用于定位用户的位置。 定位用户的位置 HTML5 Geolocation API 用于获得用户的地理位置。 鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可...

wybo521
2016/01/06
14
0
iOS-定位服务与地图应用开发

iOS-定位服务与地图应用开发 Harries Blog™2017-12-192 阅读 maptaripAppIOScatAPIIOAndroid 一,定位服务 iOS设备能提供3种不同的定位途径: 1,WiFi定位,通过查询一个WiFi路由器的地理位...

Harries Blog™
2017/12/19
0
0
iPhone手机如间谍工具 掌握你的一切行踪

iPhone手机有个鲜为人知的功能,它能记录使用者的一切行踪,然后据此推估使用者的住家与工作场所在何处。尽管苹果公司声称,这些个人信息不会外泄,但专家仍担心,这些资料或有可能成为警方或...

今时下载网
2014/10/11
66
0
iphone开发 ---- GPS

11.1 iOS定位服务 11.2 iOS地图 11.3 Web地图 来自:http://www.itboat.net/thread-55296-1-1.html 11.1 iOS定位服务 iOS中有三个定位服务组件: Wifi定位,通过查询一个Wifi路由器的地理位置...

fcsong000833
2013/02/11
0
2

没有更多内容

加载失败,请刷新页面

加载更多

JWT学习总结

官方 https://jwt.io 英文原版 https://www.ietf.org/rfc/rfc7519.txt 或 https://tools.ietf.org/html/rfc7519 中文翻译 https://www.jianshu.com/p/10f5161dd9df 1. 概述 JSON Web Token(......

冷基
今天
5
0
AOP的学习(1)

AOP 理解AOP编程思想(面向方法、面向切面) spring AOP的概念 方面 -- 功能 目标 -- 原有方法 通知 -- 对原有方法增强的方法 连接点 -- 可以用来连接通知的地方(方法) 切入点 -- 将用来插入...

太猪-YJ
今天
5
0
一张图看懂亮度、明度、光度、光亮度、明亮度

亮度、明度、光亮度,Luminance和Brightness、lightness其实都是一个意思,只是起名字太难了。 提出一个颜色模型后,由于明度的取值与别人的不同,为了表示区别所以就另想一个词而已。 因此在...

linsk1998
昨天
11
0
Python应用:python链表示例

前言 python链表应用源码示例,需要用到python os模块方法、函数和类的应用。 首先,先简单的来了解下什么是链表?链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是...

python小白1
昨天
5
0
Source Insight加载源码

Source Insight是一个图形化的源代码查看工具(当然也可以作为编译工具)。如果一个项目的源代码较多,此工具可以很方便地查找到源代码自建的依赖关系。 1.创建工程 下图为Snort源代码的文件...

天王盖地虎626
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部