- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { static NSString *ID = @"anno"; MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID]; if (annoView == nil) { annoView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID]; // 显示气泡 annoView.canShowCallout = YES; // 设置绿色 annoView.pinColor = MKPinAnnotationColorGreen; } return annoView; }
#import "ViewController.h" #import <MapKit/MapKit.h> #import "MyAnnotation.h" @interface ViewController ()<MKMapViewDelegate> @property (weak, nonatomic) IBOutlet MKMapView *mapView; @property (weak, nonatomic) IBOutlet UITextField *latitude; @property (weak, nonatomic) IBOutlet UITextField *longitude; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //设置地图的显示风格 self.mapView.mapType = MKMapTypeStandard; //设置地图可缩放 self.mapView.zoomEnabled = YES; //设置地图可滚动 self.mapView.scrollEnabled = YES; //设置地图可旋转 self.mapView.rotateEnabled = YES; //设置显示用户显示位置 self.mapView.showsUserLocation = YES; //为MKMapView设置delegate self.mapView.delegate = self; // [self locateToLatitude:23.12672 longtitude:113.395]; NSLog(@"用户当前是否位于地图中:%d",self.mapView.userLocationVisible); } - (IBAction)goClicked:(UIButton *)sender { //关闭两个文本框的虚拟键盘 // [self.latitude resignFirstResponder]; // [self.longitude resignFirstResponder]; //经度 NSString *latitudeStr = self.latitude.text; //纬度 NSString *longtitudeStr = self.longitude.text; //如果用户输入的经度、纬度为空 if (latitudeStr != nil && latitudeStr.length > 0 && longtitudeStr!= nil && longtitudeStr.length > 0) { //设置经度、纬度 [self locateToLatitude:latitudeStr.floatValue longtitude:longtitudeStr.floatValue]; } } -(void)locateToLatitude:(CGFloat)latitude longtitude:(CGFloat)longitude { //设置地图中的的经度、纬度 CLLocationCoordinate2D center = {latitude,longitude}; //也可以使用如下方式设置经度、纬度 //center.latitude = latitude; //center.longitude = longitude; //设置地图显示的范围 MKCoordinateSpan span; //地图显示范围越小,细节越清楚; span.latitudeDelta = 0.01; span.longitudeDelta = 0.01; //创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围 MKCoordinateRegion region = {center,span}; //设置当前地图的显示中心和显示范围 [self.mapView setRegion:region animated:YES]; UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)]; [self.mapView addGestureRecognizer:longPress]; } -(void)longPress:(UILongPressGestureRecognizer *)sender { CGPoint location = [sender locationInView:self.mapView]; //转换经纬度 CLLocationCoordinate2D coordinate = [self.mapView convertPoint:location toCoordinateFromView:self.mapView]; // 创建标注 MyAnnotation *annotation = [[MyAnnotation alloc]init]; annotation.coordinate = coordinate; annotation.title = @"新的标注"; annotation.subtitle = @"开发..."; [self.mapView addAnnotation:annotation]; } #pragma mark - mapView代理方法 #pragma mark 显示标注视图 -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { static NSString *annotationID = @"annotation"; MKPinAnnotationView *view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationID]; if (!view) { view = [[MKPinAnnotationView alloc]init]; } view.annotation = annotation; // view.leftCalloutAccessoryView view.pinColor = MKPinAnnotationColorGreen; view.canShowCallout = YES; return view; } #pragma mark 代理方法 -(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { CLLocationCoordinate2D coordinate = userLocation.location.coordinate; MKCoordinateSpan span = {0.001,0.001}; MKCoordinateRegion region = {coordinate,span}; //设置显示区域 [self.mapView setRegion:region animated:YES]; MyAnnotation *annotation = [[MyAnnotation alloc]init]; annotation.coordinate = coordinate; annotation.title = @"中国"; annotation.subtitle = @"好牛B的地方"; //让地图显示标注的区域 [self.mapView setCenterCoordinate:annotation.coordinate animated:YES]; } //当MKMapView显示区域将要发生改变时激发该方法 -(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { NSLog(@"地图控件的显示区域要发生改变"); } //当MKMapView显示区域改变完成时激发该方法 -(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { NSLog(@"地图控件完成了改变"); } //当地图控件MKMapView开始加载数据时激发该方法 -(void)mapViewWillStartLoadingMap:(MKMapView *)mapView { NSLog(@"地图控件开始加载地图数据"); //创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围 MKCoordinateRegion region = mapView.region; self.latitude.text = [NSString stringWithFormat:@"%f",region.center.latitude]; self.longitude.text = [NSString stringWithFormat:@"%f",region.center.longitude]; } //当MKMapView加载数据完成时激发该方法 -(void)mapViewDidFinishLoadingMap:(MKMapView *)mapView { NSLog(@"地图控件加载地图数据完成"); NSLog(@"%@",mapView); } //当MKMapView加载数据失败时激发该方法 -(void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error { NSLog(@"地图控件加载地图数据发生错误:错误信息:%@",error); } //当MKMapView开始渲染地图时激发该方法 -(void)mapViewWillStartRenderingMap:(MKMapView *)mapView { NSLog(@"地图控件开始渲染地图"); } //当MKMapView渲染地图完成时激发该方法 -(void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered { NSLog(@"地图控件渲染完成"); } @end
MyAnnotation.h文件内容
#import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface MyAnnotation : NSObject<MKAnnotation> @property(copy,nonatomic)NSString *title; @property(copy,nonatomic)NSString *subtitle; @property(assign,nonatomic)CLLocationCoordinate2D coordinate; @end
程序运行结果如下:
程序中还为MKMapView指定了delegate,因此该delegate将会响应地图位置改变、加载过程中的相关事件,所以大家可以看到Xcode的控制输出消息。
二、根据地址定位
1.地址解析与反向地址解析
地址解析:把普通用户能看懂的字符串地址转换为经度、纬度。
反向地址解析:把经度、纬度转换成普通的字符串地址。
iOS为地址解析提供了CLGeocoder工具类,该工具类提供了如下3种方法来进行地址解析和反向地址解析。
-geocodeAddressString:completionHandler: :根据给定的字符串地址进行解析,解析将会得到该地址对应的经度、纬度信息。
-geocodeAddressString:inRegion:completonHandler: :根据给定的字符串进行解析,解析将会得到该地址对应的经度、纬度信息。
-reverseGeocodeLocation:completionHandler: :根据给定的经度、纬度地址方向解析得到字符串地址。
一般来说:地址解析可能得到多个结果——这是因为全球完全可能有多个同名的地点;但反向地址解析一般只会得到一个结果——因为根据指定经度、纬度得到的地址通常是唯一的。
2.根据地址定位
根据地址定位的思路非常简单,只要如下两步即可。
(1)使用CLGeocoder根据字符串地址得到该地址的经度、纬度。
(2)根据解析得到的经度、纬度进行定位。
#import "ViewController.h" #import <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> @interface ViewController ()<CLLocationManagerDelegate> @property(strong,nonatomic)CLLocationManager *locationManager; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 创建定位管理器 self.locationManager = [[CLLocationManager alloc]init]; //获取用户授权 [self.locationManager requestAlwaysAuthorization]; //获取当前授权状态 CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) { NSLog(@"授权通过"); } else { NSLog(@"授权不通过"); } //设置代理 self.locationManager.delegate = self; //设置经度 self.locationManager.desiredAccuracy =kCLLocationAccuracyBest; //开始定位 [self.locationManager startUpdatingLocation]; } #pragma mark - 定位管理器的代理方法 -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { // NSLog(@"%@",locations); //取出位置信息 CLLocation *location = [locations lastObject]; //创建地理信息编解码对象 CLGeocoder *geoCoder = [[CLGeocoder alloc]init]; //转换位置信息 [geoCoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { // NSLog(@"%@",placemarks[0]); CLPlacemark *placeMark = placemarks[0]; NSLog(@"%@%@",placeMark.country,placeMark.locality); }]; } -(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"%@",error); } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { CLGeocoder *geoCoder = [[CLGeocoder alloc]init]; [geoCoder geocodeAddressString:@"西三旗" completionHandler:^(NSArray *placemarks, NSError *error) { if ([placemarks count] == 0) { NSLog(@"没有找到相应的地理信息"); } else { CLPlacemark *placemark = [placemarks objectAtIndex:0]; //NSLog(@"%@",placeMark); // NSLog(@"%f,%f",placeMark.location.coordinate.latitude,placeMark.location.coordinate.longitude); MKMapItem *mapItem1 = [[MKMapItem alloc]initWithPlacemark:(MKPlacemark*)placemark]; [geoCoder geocodeAddressString:@"八达岭长城" completionHandler:^(NSArray *placemarks, NSError *error) { MKMapItem *mapItem2 = [[MKMapItem alloc]initWithPlacemark:(MKPlacemark*)placemarks[0]]; //调用系统地图打开一个位置 [MKMapItem openMapsWithItems:@[mapItem1,mapItem2] launchOptions:nil]; }]; } }]; } @end
三、在地图上添加锚点
1.添加简单地锚点
对于iOS的地图而言,添加锚点只要调用MKMapView的-(void)addAnnotation:(id<MKAnnotation>)annotation方法即可,每调用一次,就像地图添加一个锚点。MKAnnotation是一个协议,该协议中定义了3个属性,用于设置和返回锚点的信息。
- coordinate:用于设置和返回锚点的位置。该属性值必须是一个CLLocationCoordinate2D结构体变量,封装了经度、纬度信息。
- title:用于设置和返回锚点的标题。
- subtitle:用于设置和返回锚点的副标题。
2.添加自定义锚点
锚点由两部分组成。
锚点信息:锚点信息包括锚点的位置、标题、副标题等信息,这些信息有MKAnnotation对象代表。
锚点控件:锚点控件决定地图上显示的锚点外观,包括锚点的图片等。
iOS为锚点控件提供了MKAnnotationView和MKPinAnnotationView,其中MKPinAnnotationView是MKAnnotationView的子类,而MKAnnotationView继承了UIView——也就是说,它只是一个可视化的UI控件。
为定制地图上的锚点,需要完成如下两步。
1.为MKMapView指定delegate对象。
2.重写delegatede-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation方法,该方法的返回值将作为地图上的锚点控件。
案例:在图上添加锚点
#import "ViewController.h" #import <MapKit/MapKit.h> #import "MyAnnoation.h" @interface ViewController ()<MKMapViewDelegate> @property(strong,nonatomic)MKMapView *mapView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 创建mapView self.mapView = [[MKMapView alloc]initWithFrame:self.view.frame]; //设置地图的类型 self.mapView.mapType = MKMapTypeStandard; [self.view addSubview:self.mapView]; self.mapView.delegate = self; //创建标注 MyAnnoation *annoation = [[MyAnnoation alloc]init]; annoation.coordinate= CLLocationCoordinate2DMake(40, 100); annoation.title = @"中国"; annoation.subtitle = @"好牛B的地方"; //添加标注 [self.mapView addAnnotation:annoation]; //让地图显示标注的区域 [self.mapView setCenterCoordinate:annoation.coordinate animated:YES]; //添加手势 UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)]; [self.mapView addGestureRecognizer:longPress]; } -(void)longPress:(UILongPressGestureRecognizer *)sender { //获取当前位置 CGPoint location = [sender locationInView:self.view]; //转换经纬度 CLLocationCoordinate2D coordinate =[self.mapView convertPoint:location toCoordinateFromView:self.mapView]; //创建标注 MyAnnoation *annotation = [[MyAnnoation alloc]init]; annotation.coordinate = coordinate; annotation.title = @"新的标注"; annotation.subtitle = @"待开发。。"; //添加标注 [self.mapView addAnnotation:annotation]; } #pragma mark - mapView代理方法 #pragma mark 显示标注视图 -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { static NSString *annotationID =@"annotation"; //先从重用队列中去找 MKPinAnnotationView *view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationID]; //如果找不到,自己创建 if (!view) { view = [[MKPinAnnotationView alloc]init]; } //设置属性 view.annotation = annotation; view.canShowCallout = YES;//显示气泡视图 view.pinColor = MKPinAnnotationColorPurple;//设置大头针颜色 //显示图片(这种方式设置图片会取代大头针) view.image = [UIImage imageNamed:@"0.png"]; //设置气泡的辅助视图(显示图片) view.leftCalloutAccessoryView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"0.png"]]; return view; } #pragma mark 选中了标注后的处理 -(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { NSLog(@"选中了标注"); } -(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view { NSLog(@"取消了标注"); } @end
四、导航请求(MKDirections)
//
// ViewController.m
// 导航
//
// Created by aliios on 16/1/4.
// Copyright © 2016年 aliios. All rights reserved.
//
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "MyAnnotation.h"
@interface ViewController ()<MKMapViewDelegate,CLLocationManagerDelegate>
@property(nonatomic,strong)MKMapView *mapView;
@property(nonatomic,strong)CLLocationManager *locationManager;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
if(![CLLocationManager locationServicesEnabled]||[CLLocationManager authorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
[self.view addSubview:self.mapView];
[self daohang];
}
#pragma mark - MKMapViewDelegate
//7.设置线路遮蔽物(红色线条)宽度,颜色:MKPolylineRenderer
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay{
MKPolylineRenderer *ployline = [[MKPolylineRenderer alloc]initWithOverlay:overlay];
ployline.fillColor = [UIColor blueColor];
ployline.strokeColor = [UIColor greenColor];
ployline.lineWidth = 2.0;
return ployline;
}
#pragma mari -Action Event
//导航
- (void)daohang{
//1.创建起始点和目的地的经纬度信息CLLocationCoordinate2D
CLLocationCoordinate2D fromDinate = CLLocationCoordinate2DMake(22.5768585, 113.863693);
CLLocationCoordinate2D toDinate = CLLocationCoordinate2DMake(22.5758801, 113.862823);
//2.创建出发点和目的点信息MKPlacemark
MKPlacemark *fromPlace = [[MKPlacemark alloc]initWithCoordinate:fromDinate addressDictionary:nil];
MKPlacemark *toPlace = [[MKPlacemark alloc]initWithCoordinate:toDinate addressDictionary:nil];
//3.根据MKPlacemark创建出发节点和目的地节点MKMapItem
MKMapItem *fromItem = [[MKMapItem alloc]initWithPlacemark:fromPlace];
MKMapItem *toItem = [[MKMapItem alloc]initWithPlacemark:toPlace];
//4.初始化导航请求,MKDirectionsRequest,在这里可以设置source,requestsAlternateRoutes等
MKDirectionsRequest *request = [[MKDirectionsRequest alloc]init];
request.source = fromItem;
request.destination = toItem;
//设置交通方式(有步行和机动车)
request.transportType = MKDirectionsTransportTypeWalking;
request.requestsAlternateRoutes = YES;
//5.创建导航对象 MKDirections
MKDirections *directions = [[MKDirections alloc]initWithRequest:request];
//6.计算线路,并将结果返回到block里
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
//提取导航中的一条线路 MKRoute
MKRoute *route = [response.routes lastObject];
//提取这条线路的每一步的信息
NSArray *stepArray = route.steps;
for (MKRouteStep *step in stepArray) {
MyAnnotation *annotation = [[MyAnnotation alloc]init];
annotation.coordinate = step.polyline.coordinate;
annotation.title = step.instructions;
annotation.subtitle = step.notice;
[self.mapView addAnnotation:annotation];
//将线路覆盖物添加到地图上
[self.mapView addOverlay:step.polyline];
}
}];
}
#pragma mark -getter setter
- (MKMapView *)mapView{
if (!_mapView) {
_mapView = [[MKMapView alloc]initWithFrame:self.view.bounds];
_mapView.mapType = 0;
_mapView.userTrackingMode = MKUserTrackingModeFollow;
_mapView.delegate = self;
}
return _mapView;
}
- (CLLocationManager *)locationManager{
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc]init];
_locationManager.distanceFilter = 10;
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.delegate = self;
}
return _locationManager;
}
@end
说一下Annotation和Overlay的区别,同样是覆盖物有什么不同呢?
Annotation和Overlay用法很相似,区别是:无论地图怎么旋转缩放,Annotation都是不跟着形变的,就像大头针一样钉在那里,而Overlay会随着地图的缩放旋转,这就是它们之间的区别。