定位于地图小程序

时间:2021-03-10 14:37:35

最近做了一个小程序模仿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>我们添加#import "MyAnnotation.h"我们自定一的一个类(标注)

定位于地图小程序

@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类型
开启iOS系统自带的地图,并用openInMapsWithLaunchOptions方法实例化mapItme2,它的参数是NSDictionary,它可以告诉地图显示哪些信息。

MKLaunchOptionsDirectionsModeKey(路线模式)它有2个值【MKLaunchOptionsDirectionsModeDriving】开车路线【MKLaunchOptionsDirectionsModeWalking】步行路线

MKLaunchOptionsMapTypeKey(地图类型)

MKLaunchOptionsMapCenterKey(地图中心点)

MKLaunchOptionsMapSpanKey(地图跨度)

MKLaunchOptionsShowsTrafficKey(交通状况)

第四个button(我的位置)功能

 

- (IBAction)reverseGeoccode:(id)sender {        [self.locationmanger startUpdatingLocation];}

 

好了到了目前为止一个小的定位程序做好了运行一下程序吧:测试一下吧

目前定位到重庆市,在text中输入beijing点击一下路线(导航)按钮看看什么效果吧

定位于地图小程序  点击后定位于地图小程序

 

程序可以正确运。

这4个按钮的功能都是独立的,他们之间没有联系。其中的代码几乎一样,所以我也就没有过多的解释(其实是我比较懒)这是我的学习笔记希望对你们有帮助。