iOS 地图源及目的地定位、划线和系统导航的使用

来源:互联网 发布:中兴软件南昌有限公司 编辑:程序博客网 时间:2024/06/11 14:26

最近在做一些关于地图的使用,才发现以前了解的东西很浅,很多细节上的东西没有弄清楚,在这里我想记录一下。好记性不如烂笔头,何况我这烂记性,就更得需要一个好笔头了。废话就不多说,下面就是我在使用系统地图的思路和代码。新手上路,不另指教!

Step1
导入 Mapkit.framework 框架

这里写图片描述

Step2
引入头文件

#import <MapKit/MapKit.h> //地图框架#import <CoreLocation/CoreLocation.h> //定位和编码框架

如果只是使用定位,则引入 CoreLocation/CoreLocation.h 即可,需要显示地图则引入MapKit/MapKit.h

Step3
遵守协议和指定代理

@interface MapViewController ()<CLLocationManagerDelegate, MKMapViewDelegate, UITextFieldDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager; //定位管理器@property (nonatomic, strong) CLGeocoder *geocoder; //创建一个地理编码器,来实现编码和反编码

懒加载创建

- (CLLocationManager *)locationManager{    if (!_locationManager) {        self.locationManager = [[CLLocationManager alloc] init];    }    return _locationManager;}- (CLGeocoder *)geocoder{    if (!_geocoder) {        self.geocoder = [[CLGeocoder alloc] init];    }    return _geocoder;}

Step4
显示地图并定位

- (void)viewDidLoad {    [super viewDidLoad];    [self startLocationUpdate];    self.PYMapView.mapType = MKMapTypeStandard;//选择显示地图的样式    self.PYMapView.delegate = self;//指定代理    /*     *[self.PYMapView setShowsUserLocation:YES];//显示用户位置的小圆点     */}#pragma mark --- 开始定位- (void)startLocationUpdate{    //判断用户在设置里面是否打开定位服务    if (![CLLocationManager locationServicesEnabled]) {        NSLog(@"请在设置里面打开定位服务");    }    else{        //获取用户对应用的授权        if ([ CLLocationManager authorizationStatus]== kCLAuthorizationStatusNotDetermined) {            NSLog(@"请求授权");            [self.locationManager requestAlwaysAuthorization];        }        else{            //用户位置追踪(用户位置追踪用于标记用户当前位置,此时会调用定位服务)            self.PYMapView.userTrackingMode = MKUserTrackingModeFollow;//即可以在显示地图的时候定位到用户的位置            self.locationManager.delegate = self;            //设置定位的经纬度精度            _locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;            //设置定位频率,每隔多少米定位一次            CLLocationDistance distance = 10.0;            _locationManager.distanceFilter = distance;            //开始定位            [self.locationManager startUpdatingLocation];        }    }}#pragma mark --- CLLocationDelegate- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{    /*     *locations  数组里面存储CLLoction对象, 一个对象代表一个地址     *     *location 中存储  经度 coordinate.longitude、纬度 coordinate.latitude、航向 location.course、速度 location.speed、海拔 location.altitude     */    CLLocation *location = locations.firstObject;    CLLocationCoordinate2D coordinate = location.coordinate;    NSLog(@"经度:%f,纬度:%f,海拔:%f,航向:%f,行走速度:%f",coordinate.longitude,coordinate.latitude,location.altitude,location.course,location.speed);    [self getAddressByLatitude:coordinate.latitude longitude:coordinate.longitude];    [self.locationManager stopUpdatingLocation];}

这里就可以根据打印结果获取用户位置了,但是你会发现,然而并本是这样
原因:

 iOS8 以后使用系统地图都需要在 info.plist中加入 一下两个字段 NSLocationAlwaysUsageDescription   YES   //一直使用地位服务 NSLocationWhenInUseDescription     YES   //打开应用的时候使用地位服务

根据你的需求添加对应的字段

注: 我使用的是真机(6SP),模拟器地图地位使用不方便。

打印结果:
这里写图片描述

效果图1:
这里写图片描述

Step5
使用 GE 进行编码和反编码

/**编码与反编码的作用在于: 输入某个位置,根据你输入的位置编码成地理坐标,或根据定位获得的地理坐标反编码成具体的位置信息*/#pragma mark 根据地名确定地理坐标-(void)getCoordinateByAddress:(NSString *)address{    //通过自定义的NSString的延展来判断字符差中不符合要求的情况    if ([NSString isBlankString:address]) {        NSLog(@"输入内容不正确");        return;    }    //地理编码    [self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {        //取得第一个地标,地标中存储了详细的地址信息,注意:一个地名可能搜索出多个地址        CLPlacemark *placemark=[placemarks firstObject];        CLLocation *location=placemark.location;//位置        CLRegion *region=placemark.region;//区域        NSDictionary *addressDic= placemark.addressDictionary;//详细地址信息字典,包含以下部分信息        /*        NSString *name=placemark.name;//地名        NSString *thoroughfare=placemark.thoroughfare;//街道        NSString *subThoroughfare=placemark.subThoroughfare; //街道相关信息,例如门牌等        NSString *locality=placemark.locality; // 城市        NSString *subLocality=placemark.subLocality; // 城市相关信息,例如标志性建筑        NSString *administrativeArea=placemark.administrativeArea; // 州        NSString *subAdministrativeArea=placemark.subAdministrativeArea; //其他行政区域信息        NSString *postalCode=placemark.postalCode; //邮编        NSString *ISOcountryCode=placemark.ISOcountryCode; //国家编码        NSString *country=placemark.country; //国家        NSString *inlandWater=placemark.inlandWater; //水源、湖泊        NSString *ocean=placemark.ocean; // 海洋        NSArray *areasOfInterest=placemark.areasOfInterest; //关联的或利益相关的地标         */        NSLog(@"位置:%@,区域:%@,详细信息:%@",location,region,addressDic);        NSLog(@"%f-- %f", location.coordinate.latitude, location.coordinate.longitude);        //添加大头针        [self addAnnotationWith:placemark.location.coordinate.latitude Longtitude:placemark.location.coordinate.longitude Placemark:placemark];        //设置为地图中心        [self setCenterWithPlaceMark:placemark];    }];}#pragma mark 根据坐标取得地名-(void)getAddressByLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude{    //反地理编码    CLLocation *location=[[CLLocation alloc]initWithLatitude:latitude longitude:longitude];    [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {        CLPlacemark *placemark=[placemarks firstObject];        NSLog(@"详细信息:%@",placemark.addressDictionary);         self.useLocation = placemark.name;        NSLog(@"%@",self.useLocation);    }];}
#pragma mark --- MapViewDelegate  用户位置持续更新,该方法会一直被调用- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{    NSLog(@"%@",userLocation);    /*    *在需要持续更新用户位置的时候才打开下面的代码    */    /*    CLLocationCoordinate2D center = userLocation.coordinate;//中心    MKCoordinateSpan span = MKCoordinateSpanMake(0.005, 0.005);//范围    MKCoordinateRegion region = MKCoordinateRegionMake(center, span);    [self.PYMapView setRegion:region animated:YES];    */}//设置地图的中心- (void)setCenterWithPlaceMark:(CLPlacemark *)placemark{    CLLocationCoordinate2D center = placemark.location.coordinate;//中心    MKCoordinateSpan span = MKCoordinateSpanMake(0.003, 0.003);//范围    MKCoordinateRegion regiontion = MKCoordinateRegionMake(center, span);    [self.PYMapView setRegion:regiontion animated:YES];}

现在 就可以定位到用户位置了

Step6
添加大头针 (自定义大头针,icon, title,subtitle)
创建一个继承于 NSObject的类,如下所示:

/* * 自定义大头针 类 */#import <Foundation/Foundation.h>#import <MapKit/MapKit.h>@interface PYAnnotation : NSObject<MKAnnotation>@property (nonatomic, assign) CLLocationCoordinate2D coordinate;//坐标@property (nonatomic, copy) NSString *title;//标题@property (nonatomic, copy) NSString *subtitle;//详情@property (nonatomic,strong) UIImage *image;//自定义一个图片属性在创建大头针视图时使用@end

记住要遵守 协议, 这样自定义大头针就大功告成了? 幼稚!
我们还需要到控制器去实现一个必须实现的方法,才可以让自定义大头针显示出来,不然会是系统默认的大头针样式

方法如下:

#pragma mark - 地图控件代理方法#pragma mark 显示大头针时调用,注意方法中的annotation参数是即将显示的大头针对象, 只有该方法实现了才可以显示自定义的大头针图片-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{    //由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图    if ([annotation isKindOfClass:[PYAnnotation class]]) {        static NSString *key1=@"PYAnnotation";        MKAnnotationView *annotationView = [_PYMapView dequeueReusableAnnotationViewWithIdentifier:key1];        //如果缓存池中不存在则新建        if (!annotationView) {            annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key1];            annotationView.canShowCallout = true;//允许交互点击            annotationView.calloutOffset = CGPointMake(0, 1);//定义详情视图偏移量            annotationView.leftCalloutAccessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"car"]];//定义详情左侧视图        }        //修改大头针视图        //重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置)        annotationView.annotation = annotation;        annotationView.image = ((PYAnnotation *)annotation).image;//设置大头针视图的图片        return annotationView;    }    else {        return nil;    }}

现在就可以使用自定义的大头针了
//添加大头针的方法

/**方法一:  使用懒加载创建自定义大头针, 哪里需要哪里使用, 就可以保持地图上面只出现一个大头针了(使用场景:在搜索位置的时候,多次输入搜索结果,但只需要为最新的位置添加大头针)*/- (PYAnnotation *)annotation{    if (!_annotation) {        self.annotation = [[PYAnnotation alloc] init];    }    return _annotation;}- (void)addAnnotationWith:(CLLocationDegrees)latitude Longtitude:(CLLocationDegrees)longtitude Placemark:(CLPlacemark *)placemark{    CLLocationCoordinate2D location= placemark.location.coordinate;    self.annotation.title = placemark.locality;    self.annotation.subtitle = placemark.name;    self.annotation.coordinate = location;    self.annotation.image = [UIImage imageNamed:@"flag1"];//大头针图片    [_PYMapView addAnnotation:_annotation];}/**方法二: 直接创建,每次调用该方法都会创建一个自定义的大头针(使用场景:需要起点、和终点同时添加大头针时,调用该方法便可)*/- (void)AnnotationWith:(CLLocationDegrees)latitude Longtitude:(CLLocationDegrees)longtitude Placemark:(CLPlacemark *)placemark ImageName:(NSString *)imageName{    PYAnnotation *annotation = [[PYAnnotation alloc] init];    CLLocationCoordinate2D location= placemark.location.coordinate;    annotation.title = placemark.locality;    annotation.subtitle = placemark.name;    annotation.coordinate = location;    annotation.image = [UIImage imageNamed:imageName];//大头针图片    [_PYMapView addAnnotation:annotation];}

效果图2:
这里写图片描述

Step7
划线,连接输入的终点和起点

/**根据输入的内容进行起点和终点的编码*/- (void)queryPathWayWithType:(NSString *)type{    NSLog(@"去这里");    [MBProgressHUD showHUDAddedTo:self.view animated:YES];    //开始编码(先起点)    [self.geocoder geocodeAddressString:self.userPalece completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {        CLPlacemark *sourcePlaceMark = [placemarks firstObject];        if (sourcePlaceMark == nil) {            return ;        }        NSLog(@"起点编码成功");        //开始编码(终点)        [self.geocoder geocodeAddressString:self.destionPlace completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {            CLPlacemark *destinationPlaceMark = [placemarks firstObject];            if (destinationPlaceMark == nil) {                return ;            }            NSLog(@"编码成功");            if ([type isEqualToString:@"draw"]) {                //两个地标都找到, 就开始划线                [self drawLineWithSource:sourcePlaceMark To:destinationPlaceMark];            }            if ([type isEqualToString:@"navi"]) {                //开始导航                [self startNavigationWithStartPlacemark:sourcePlaceMark endPlacemark:destinationPlaceMark];            }        }];      }];}/**根据编码结果 进行划线*/#pragma mark ---- 划线方法 把起点和终点用线连接,选择不同的模式就会有不同的路线。- (void)drawLineWithSource:(CLPlacemark *)sourcePlaceMark To:(CLPlacemark *)destinationPlaceMark{    if (sourcePlaceMark == nil || destinationPlaceMark == nil) {        return;    }    //1、搞清楚方法    MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];    request.requestsAlternateRoutes = YES;//进入Api自己看    /*    *MKDirectionsTransportTypeAutomobile //驾车    *MKDirectionsTransportTypeWalking //步行    *MKDirectionsTransportTypeTransit //公交    *MKDirectionsTransportTypeAny //默认    */    request.transportType = MKDirectionsTransportTypeWalking;    //2、设置起点    MKPlacemark *sourcePM = [[MKPlacemark alloc] initWithPlacemark:sourcePlaceMark];//定位坐标转化为地图坐标    request.source = [[MKMapItem alloc] initWithPlacemark:sourcePM];    //self.sourcePlaceMark = sourcePM;    //3、设置终点    MKPlacemark *destinationPM = [[MKPlacemark alloc] initWithPlacemark:destinationPlaceMark];    request.destination = [[MKMapItem alloc] initWithPlacemark:destinationPM];    //self.destinationPlaceMark = destinationPM;    //4、根据起点和终点开始请求(创建请求方向)    MKDirections *directions = [[MKDirections alloc] initWithRequest:request];    //5、发送请求    [directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {        if (error) {//            [MBProgressHUD hideHUD];            [MBProgressHUD hideHUDForView:self.view animated:YES];            NSLog(@"计算路线错误");            return ;        }        NSLog(@"%@", response.routes);        //计算正确, 给计算出来的路线划线        MKRoute *route = response.routes[0];        self.oldLine = route.polyline;        [self.PYMapView addOverlay:route.polyline];//        for (MKRoute *route in response.routes) {//            //            //拿出返回结果中存储的所有路线中的需要路线//            [self.PYMapView addOverlay:route.polyline];//            self.oldLine = route.polyline;//        }        //添加大头针        [self AnnotationWith:sourcePlaceMark.location.coordinate.latitude Longtitude:sourcePlaceMark.location.coordinate.longitude Placemark:sourcePlaceMark ImageName:@"start"];        [self AnnotationWith:destinationPlaceMark.location.coordinate.latitude Longtitude:destinationPlaceMark.location.coordinate.longitude Placemark:destinationPlaceMark ImageName:@"dest"];        [MBProgressHUD hideHUDForView:self.view animated:YES];        //设置中心        [self setCenterWithPlaceMark:sourcePlaceMark];    }];}#pragma mark ==== 划线的代理方法- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{    //在该方法里面划线,设置线的属性    MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];    renderer.lineWidth = 3;    renderer.strokeColor = [UIColor blueColor];    return renderer;}

效果图3:
这里写图片描述

Step8
跳转系统地图 导航

/** *  利用地标位置开始设置导航 * startPlacemark 开始位置的地标 *  endPlacemark   结束位置的地标 */-(void)startNavigationWithStartPlacemark:(CLPlacemark *)startPlacemark endPlacemark:(CLPlacemark*)endPlacemark{    //0,创建起点    MKPlacemark * startMKPlacemark = [[MKPlacemark alloc]initWithPlacemark:startPlacemark];    //0,创建终点    MKPlacemark * endMKPlacemark = [[MKPlacemark alloc]initWithPlacemark:endPlacemark];    //1,设置起点位置    MKMapItem * startItem = [[MKMapItem alloc]initWithPlacemark:startMKPlacemark];    //2,设置终点位置    MKMapItem * endItem = [[MKMapItem alloc]initWithPlacemark:endMKPlacemark];    //3,起点,终点数组    NSArray * items = @[startItem ,endItem];    //4,设置地图的附加参数,是个字典    NSMutableDictionary * dictM = [NSMutableDictionary dictionary];    //导航模式(驾车,步行)    if ([self.pathType isEqualToString:@"D"]) {        dictM[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeDriving;    }    else if ([self.pathType isEqualToString:@"W"]) {        dictM[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeWalking;    }    else{        dictM[MKLaunchOptionsDirectionsModeKey] = MKLaunchOptionsDirectionsModeTransit;    }    //地图显示的模式    dictM[MKLaunchOptionsMapTypeKey] = MKMapTypeStandard;    //只要调用MKMapItem的open方法,就可以调用系统自带地图的导航    //Items:告诉系统地图从哪到哪    //launchOptions:启动地图APP参数(导航的模式/是否需要先交通状况/地图的模式/..)    [MBProgressHUD hideHUDForView:self.view animated:YES];    [MKMapItem openMapsWithItems:items launchOptions:dictM];}

效果图4:
这里写图片描述

End 这就是我使用系统地图是的代码和效果,在这里自己记录一下,也希望可以帮到需要的人,如有问题,请指正!
个人邮箱:18729030047@163.com

0 0
原创粉丝点击