热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

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

Step1
导入 Mapkit.framework 框架

这里写图片描述

Step2
引入头文件

#import  //地图框架
#import  //定位和编码框架

如果只是使用定位,则引入 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 *)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 
#import 

@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)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 * _Nullable placemarks, NSError * _Nullable error) {
        CLPlacemark *sourcePlaceMark = [placemarks firstObject];
        if (sourcePlaceMark == nil) {
            return ;
        }
        NSLog(@"起点编码成功");

        //开始编码(终点)
        [self.geocoder geocodeAddressString:self.destionPlace completionHandler:^(NSArray * _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)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


推荐阅读
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • ScrollView嵌套Collectionview无痕衔接四向滚动,支持自定义TitleView
    本文介绍了如何实现ScrollView嵌套Collectionview无痕衔接四向滚动,并支持自定义TitleView。通过使用MainScrollView作为最底层,headView作为上部分,TitleView作为中间部分,Collectionview作为下面部分,实现了滚动效果。同时还介绍了使用runtime拦截_notifyDidScroll方法来实现滚动代理的方法。具体实现代码可以在github地址中找到。 ... [详细]
  • iOS Swift中如何实现自动登录?
    本文介绍了在iOS Swift中如何实现自动登录的方法,包括使用故事板、SWRevealViewController等技术,以及解决用户注销后重新登录自动跳转到主页的问题。 ... [详细]
  • PDO MySQL
    PDOMySQL如果文章有成千上万篇,该怎样保存?数据保存有多种方式,比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择,做We ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
author-avatar
漂漂雪飘飘业_348
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有