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