定位于地图小程序
导读:
最近做了一个小程序模仿GPS,实现的基本功能有定位,地点查找,与导航等等,主要涉及到编码与反编码,(还有就是系统自己的)听着很麻烦,其实当自己理解了发现也不是那么难。废话也不多说。涉及到的函数根据代码来介绍。
我们先看看Main.storyboard,下图所示,结构非常简单,几个button,text,一个mapview。
介绍一下这个几个按钮的作用:
编码:就是把我们的输入的地点名字来获得它对应的经纬度以及详细信息。
反编码:就是把经纬度进行解析,获取这个经纬度对应的一些信息比如(城市,街道,乡。省)。
我的位置:定位到自己的位置。
路线(导航):根据自己的位置和搜索的地名来设计路线。
在我们写代码前我们还需要做几件事:
1.授权:打开工程中info.plist属性列表文件,添加2个键Privacy - Location Usage Description和Privacy - Location Always Usage Description。
2添加2个框架:如下图
在这里介绍一下这2框架,为什么我们要用到它:
CoreLocation框架:在IOS中,定位服务API主要使用的就是CoreLocation框架,在我们编制定位程序时,就要用到它的3个类。
1)CLLocationManager:他的作用在于可以为我们提供位置的一些信息(经纬度),还可以根据它来监控当时人所在的区域,前进方向。
2)CLLocationManagerDelegate:这是一个委托协议,我们在定位时需要用到它的一些方法。(下面在详细介绍)
3)CLLocation:封装了位置和高度信息。
MapKit框架:在IOS开发中,MKMapView类是开发地图应用的核心。使用Map Kit API就要导入MapKit框架。
-------这些都是小编我的自己感悟,并不代表全部,小编不可能把概念,功能全部写下来,如果想了解更加详细。请去网上查资料
目前介绍到这里,接下来进入代码环节
首先自定一个标注类:用来显示地址的信息:
MyAnnotation.h
#import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface MyAnnotation : NSObject<MKAnnotation> @property (nonatomic,readwrite)CLLocationCoordinate2D coordinate;//地理坐标 @property(nonatomic,copy) NSString * street;//街道信息 @property(nonatomic,copy) NSString * city;//城市 @property(nonatomic,copy) NSString * state;//州,省,市; @property(nonatomic,copy) NSString * zip; //邮编
MyAnnotation.m
#import "MyAnnotation.h" @implementation MyAnnotation -(NSString *)title{ return @"您的位置"; } -(NSString *)subtitle{ NSMutableString *ret=[NSMutableString new]; if (_state) { [ret appendString:_state]; } if (_city) { [ret appendString:_city]; } if (_city&&_state) { [ret appendString:@","]; } if (_street&&(_city||_state||_city)) { [ret appendString:@"~"]; } if (_street) { [ret appendString:_street]; } if (_zip ) { [ret appendFormat:@",%@",_zip]; } return ret; } @end
ViewController.h
#import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> #import <MapKit/MapKit.h> #import "MyAnnotation.h" @interface ViewController : UIViewController //地名 @property (weak, nonatomic) IBOutlet UITextField *txtKey; //编码 - (IBAction)geocdeQuery:(id)sender; //MkMapview 显示地图 @property (weak, nonatomic) IBOutlet MKMapView *mapview; //我的位置 - (IBAction)reverseGeoccode:(id)sender; //经度 @property (weak, nonatomic) IBOutlet UITextField *JingDuText; //纬度 @property (weak, nonatomic) IBOutlet UITextField *WeiDuText; //反编码 - (IBAction)reverseGeoccodeTwo:(id)sender; //路线(导航) - (IBAction)geocdeQueryTwo:(id)sender; @end
定义的几个控件与Main.storyboard一致的可以结合着看
#import <CoreLocation/CoreLocation.h>引入了CLLocation模块。
#import <MapKit/MapKit.h>我们添加MapKit框架
#import "MyAnnotation.h"我们自定一的一个类(标注)
让我们来看看viewcontroller.m的代码
@interface ViewController ()<CLLocationManagerDelegate> @property(nonatomic,strong)CLLocationManager *locationmanger; @property(nonatomic,strong)CLLocation *currlocation; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _mapview.mapType=MKMapTypeStandard;//标注地图类型 // [_mapview setUserTrackingMode:MKUserTrackingModeFollow animated:YES]; // _mapview.userLocation.title=@"您的位置:"; // _mapview.userLocation.subtitle=@"xxxxxxxxxxx"; _mapview.delegate=self;// self.locationmanger=[[CLLocationManager alloc]init]; //设置委托对象为自己 self.locationmanger.delegate=self; //要求CLLocationManager对象的返回结果尽可能的精准 self.locationmanger.desiredAccuracy=kCLLocationAccuracyBest; //设置距离筛选器distanceFilter,下面表示设备至少移动1000米,才通知委托更新 self.locationmanger.distanceFilter=1000.0f; //使用中授权 [self.locationmanger requestWhenInUseAuthorization]; //永久授权 [self.locationmanger requestAlwaysAuthorization]; [self.locationmanger startUpdatingLocation]; }
CLLocationManagerDelegate申明遵守的协议。
地图显示的类型共有3种,他们是在枚举类型MKMapType中定义的(Standard)标注地图类型,(Satellite)卫星地图类型,(Hybrid)混合地图类型。
_mapview.delegate=self:将当前试图控制器负值给地图视图的delegate属性。这样地图视图会回掉viewcontroller,如果失败- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error调用这个方法;
//回调viewcontroller失败: - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error{ NSLog(@"error:%@",[error description]); }
[self.locationmanger startUpdatingLocation];//开始定位
开始定位后就要检查权限,是否允许定位服务:
//CLAuthorizationStatus枚举是定位的时候关于授权状态的一个枚举 -(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{ NSLog(@"status----->%d",status); if (status == kCLAuthorizationStatusAuthorizedAlways) { NSLog(@"定位服务授权状态已经被用户允许在任何状态下获取位置信息。一直开启定位"); }else if (status == kCLAuthorizationStatusAuthorizedWhenInUse){ NSLog(@"定位服务授权状态仅被允许在使用应用程序的时候,当使用时开启定位"); }else if (status == kCLAuthorizationStatusDenied){ NSLog(@"定位服务授权状态已经被用户明确禁止,或者在设置里的定位服务中关闭"); }else if (status == kCLAuthorizationStatusRestricted){ NSLog(@"无法使用定位服务,该状态用户无法改变"); }else if (status == kCLAuthorizationStatusNotDetermined){ NSLog(@"用户尚未做出决定是否启用定位服务,用户从未选择过权限"); } }
如果我们的定位服务已经开启,也设置了CLLocationManagerDelegate的属性delegate,就会回掉委托方法,如果定位失败:
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{ NSLog(@"定位失败:%@",error ); }
定位成功:
//定位成功 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{ //取出数组的第一个信息 _currlocation=[locations firstObject]; //停止定位 [self.locationmanger stopUpdatingLocation]; //经纬度 NSString *lati=[NSString stringWithFormat:@"%3.5f",_currlocation.coordinate.latitude]; NSString *longi=[NSString stringWithFormat:@"%3.5f",_currlocation.coordinate.longitude]; //打印出经纬度 NSLog(@"========%@---%@============",lati,longi); //反编码 CLGeocoder*geocoder=[[CLGeocoder alloc]init]; [geocoder reverseGeocodeLocation:_currlocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { // if ([placemarks count]>0) { CLPlacemark *placemark=placemarks[0]; //地标字典,从地标集合中取出一个地标对象,循环便利就好 NSDictionary *addressDictionary=placemark.addressDictionary; //从字典中取出街道。城市,省等信息 NSString *address=[addressDictionary objectForKey:(NSString*)NSTextCheckingStreetKey]; address=address==nil ? @"" :address; NSString *state=[addressDictionary objectForKey:(NSString *)NSTextCheckingStateKey]; state=state==nil ? @"" :state; NSString *city=[addressDictionary objectForKey:(NSString *)NSTextCheckingCityKey]; city=city==nil ? @"" :city; //把街道,城市,省 等一些信息组合起来 NSString *allname=[NSString stringWithFormat:@"%@%@%@",state,address,city]; //用MKCoordinateRegionMakeWithDistance建立一个结构体,调整地图位置和缩放比例 MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(placemark.location.coordinate, 10000, 10000); [_mapview setRegion:viewRegion animated:YES];//显示动画 //标注显示出来 MyAnnotation*annotation=[[MyAnnotation alloc]init]; annotation.state=allname; annotation.coordinate=placemark.location.coordinate; [_mapview addAnnotation:annotation]; }]; }
这串代码其实也不用介绍什么了小编在在注释里都写的非常清楚了。提几个注意的
在这串代码中我用的是反编码,是通过经纬度查找地点的信息,
reverseGeocodeLocation: completionHandler(反编码)
[_mapview addAnnotation:annotation];把annotation添加到地图视图上,这个方法一单被调用会回掉-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
//在地图上添加标注时回调 -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{ MKPinAnnotationView*annnotayionview=(MKPinAnnotationView *)[_mapview dequeueReusableAnnotationViewWithIdentifier:@"PIN_ANNOTATION"]; if (annnotayionview==nil) { annnotayionview=[[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:@"PIN_ANNOTATION"]; } //黑色 annnotayionview.pinTintColor=[UIColor blackColor]; //是否动画 annnotayionview.animatesDrop=YES; //点击大头针会出现一个气泡,这个气泡回现实当前地址的信息 annnotayionview.canShowCallout=YES; return annnotayionview; }
红色部分代码的意思就是用dequeueReusableAnnotationViewWithIdentifier方法通过一个可重用的标识符“PIN_ANNOTATION”获得MKPinAnnotationView对象。在判断这个对象是否存在,如果不在(==nil)就重新创建一个
initWithAnnotation:reuseIdentifier(通过这个函数创建)
现在就来运行一下看看效果如何:
好了目前实现一个基本的定位功能已经实现了
我们来实现我们的第一个button(编码) 的功能:通过输入的地名来查询
- (IBAction)geocdeQuery:(id)sender { if (_txtKey.text==nil|| _txtKey.text.length==0) { //判断输入的地址是否符合规定 return; } // [self.locationmanger startUpdatingLocation]; CLGeocoder*geocoder=[[CLGeocoder alloc]init];// 声明GLGeocoder对象并初始化 //开始编码 [geocoder geocodeAddressString:_txtKey.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { NSLog(@"------->%lu",[placemarks count]); if ([placemarks count]>0) { [_mapview removeAnnotations:_mapview.annotations]; for (int i=0; i<[placemarks count]; i++) { CLPlacemark *placemark=placemarks[i]; //用MKCoordinateRegionMakeWithDistance建立一个结构体,调整地图位置和缩放比例 MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(placemark.location.coordinate, 10000, 10000); [_mapview setRegion:viewRegion animated:YES];//设置动画效果 MyAnnotation*annotation=[[MyAnnotation alloc]init]; annotation.street=placemark.thoroughfare; annotation.city=placemark.locality; annotation.state=placemark.administrativeArea; annotation.zip=placemark.postalCode; annotation.coordinate=placemark.location.coordinate; [_mapview addAnnotation:annotation];//把annotation添加到地图视图上,这个方法一单被调用会回掉-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation //获取地标经纬度信息 CLLocationCoordinate2D coordinate=placemark.location.coordinate; //从text中显示出来 self.JingDuText.text=[NSString stringWithFormat:@"%3.5f",coordinate.latitude]; self.WeiDuText.text=[NSString stringWithFormat:@"%3.5f",coordinate.longitude]; } } }]; }
geocodeAddressString:completionHandler;方法进行地理信息编码查询,查询的结果placemarks是一个NSArray类型
在上面我们介绍了一下反编码,现在我来介绍一下编码
geocodeAddressDictionary:completionHandler: 通过指定一个地址信息字典对象参数进行查询
geocodeAddressString:completionHandler:通过指定一个地址信息字符串参数进行查询
geocodeAddressString:inRegion:completionHandler:通过制定地址信息字符串和查询范围进行查询
[_mapview removeAnnotations:_mapview.annotations]:移除地图上的标记点,否则反复查询地图上的标记点会越来越多
第二个button(反编码)的功能
- (IBAction)reverseGeoccodeTwo:(id)sender { //把经纬度提取出来 CLLocation *myLocal=[[CLLocation alloc]initWithLatitude:[self.JingDuText.text doubleValue] longitude:[self.WeiDuText.text doubleValue]]; CLGeocoder *grocode=[[CLGeocoder alloc]init]; [grocode reverseGeocodeLocation:myLocal completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { CLPlacemark *placemark=placemarks[0]; NSDictionary *addressDictionary=placemark.addressDictionary; NSString *address=[addressDictionary objectForKey:(NSString*)NSTextCheckingStreetKey]; address=address==nil ? @"" :address; NSString *state=[addressDictionary objectForKey:(NSString *)NSTextCheckingStateKey]; state=state==nil ? @"" :state; NSString *city=[addressDictionary objectForKey:(NSString *)NSTextCheckingCityKey]; city=city==nil ? @"" :city; NSString *allname=[NSString stringWithFormat:@"%@%@%@",state,address,city]; self.txtKey.text=city; MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(placemark.location.coordinate, 10000, 10000); [_mapview setRegion:viewRegion animated:YES]; MyAnnotation*annotation=[[MyAnnotation alloc]init]; annotation.state=allname; annotation.coordinate=placemark.location.coordinate; [_mapview addAnnotation:annotation]; }]; }
第3个buttom(路线(导航))的功能
- (IBAction)geocdeQueryTwo:(id)sender { if (_txtKey.text==nil|| _txtKey.text.length==0) { //判断输入的地址是否符合规定 return; } //CLGeocoder类中有几个方法,一个是把经纬度转化成大家能看懂的信息,比如:city,county,街道等等(反编码),CLGeocoder类中的其他几个方法也可以把city,county等信息直接转化为坐标(编码) CLGeocoder*geocoder=[[CLGeocoder alloc]init];// 声明GLGeocoder对象并初始化 //开始编码 [geocoder geocodeAddressString:_txtKey.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { NSLog(@"------->%lu",[placemarks count]); if ([placemarks count]>0) { CLPlacemark *placemark=placemarks[0]; //获得该地点的坐标 CLLocationCoordinate2D coordinate=placemark.location.coordinate; //addressDictionary是该地点的信息通过placemark.addressDictionary获得 NSDictionary *adress=placemark.addressDictionary; //实例化MKPlacemark对象initWithCoordinate的参数是坐标 MKPlacemark *place=[[MKPlacemark alloc]initWithCoordinate:coordinate addressDictionary:adress]; //设置路线 NSDictionary *options=[[NSDictionary alloc]initWithObjectsAndKeys:MKLaunchOptionsDirectionsModeDriving,MKLaunchOptionsDirectionsModeKey, nil]; MKMapItem *mapItme2=[[MKMapItem alloc]initWithPlacemark:place]; [mapItme2 openInMapsWithLaunchOptions:options]; } }]; }
MKMapItem *mapItme2=[[MKMapItem alloc]initWithPlacemark:place]
实例化MkMapItme MkMapItme类封装了地图上一个点的信息类。我们需要在地图上显示的点封装到MKMApItme对象中,构造器initWithPlacemark:的参数是MKPlacemark类型
[mapItme2 openInMapsWithLaunchOptions:options]
开启iOS系统自带的地图,并用openInMapsWithLaunchOptions方法实例化mapItme2,它的参数是NSDictionary,它可以告诉地图显示哪些信息。
MKLaunchOptionsDirectionsModeKey(路线模式)它有2个值【MKLaunchOptionsDirectionsModeDriving】开车路线【MKLaunchOptionsDirectionsModeWalking】步行路线
MKLaunchOptionsMapTypeKey(地图类型)
MKLaunchOptionsMapCenterKey(地图中心点)
MKLaunchOptionsMapSpanKey(地图跨度)
MKLaunchOptionsShowsTrafficKey(交通状况)
第四个button(我的位置)功能
- (IBAction)reverseGeoccode:(id)sender { [self.locationmanger startUpdatingLocation]; }
好了到了目前为止一个小的定位程序做好了运行一下程序吧:测试一下吧
目前定位到重庆市,在text中输入beijing点击一下路线(导航)按钮看看什么效果吧
点击后
程序可以正确运。
这4个按钮的功能都是独立的,他们之间没有联系。其中的代码几乎一样,所以我也就没有过多的解释(其实是我比较懒😄)这是我的学习笔记希望对你们有帮助。
------------------------注:以上代码仅限参考。