地图系列之二 MapKit框架
简介
- MapKit框架使用前提
- 导入框架(Xcode5之后可以省略,前提:在代码里面使用了框架中某个类创建了一个对象!!!!)
- 导入主头文件
#import <MapKit/MapKit.h>
- 常见错误:
- 在SB中拖了一个MKMapView,也导入了主头文件,但是在运行时会报错,因为代码中没有使用该类的任何一个方法,所以该框架实际上还未自动导入(当应用程序第一次使用该类时,会导入)
- MapKit框架使用须知
- MapKit框架中所有数据类型的前缀都是MK
- MapKit有一个比较重要的UI控件 :MKMapView,专门用于地图显示
MKMapView
- 地图的简单展示
- 在SB中拖入一个MKMapView控件,布局,属性脱线到控制器中
- 在控制器中导入
#import <MapKit/MapKit.h>
- 运行即可展示了
MKMapView常用的属性及方法
-
常用属性:
//是否允许缩放 @property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled; //是否可滚动 @property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled; //是否可旋转 rotateEnabled @property (nonatomic, getter=isRotateEnabled) BOOL rotateEnabled; 是否显示指南针(iOS9.0) @property (nonatomic) BOOL showsCompass; //是否显示比例尺(iOS9.0) @property (nonatomic) BOOL showsScale; //是否显示交通(iOS9.0) @property (nonatomic) BOOL showsTraffic; //是否显示建筑(iOS9.0) @property (nonatomic) BOOL showsBuildings; //是否显示用户位置 @property (nonatomic) BOOL showsUserLocation; //获取用户当前的大头针模型 @property (nonatomic, readonly) MKUserLocation *userLocation; //设置地图的类型 @property (nonatomic) MKMapType mapType; /* MKMapType枚举值: MKMapTypeStandard :普通地图 MKMapTypeSatellite :卫星云图 MKMapTypeHybrid :混合模式(普通地图覆盖于卫星云图之上 ) MKMapTypeSatelliteFlyover: 3D立体卫星 (iOS9.0) MKMapTypeHybridFlyover: 3D立体混合(iOS9.0) */ //跟踪并显示用户位置的方式 @property (nonatomic) MKUserTrackingMode userTrackingMode; /* MKUserTrackingModeNone :不跟踪用户的位置 MKUserTrackingModeFollow :跟踪并在地图上显示用户的当前位置 MKUserTrackingModeFollowWithHeading :跟踪并在地图上显示用户的当前位置,地图会跟随用户的前进方向进行旋转 */
设置地图的显示
- 通过MKMapView的下列属性以及方法,可以设置地图显示的位置和区域
-
设置地图的中心点位置
@property (nonatomic) CLLocationCoordinate2D centerCoordinate; - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
-
设置地图的显示区域(地图所显示的跨度)
@property (nonatomic) MKCoordinateRegion region; - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;
-
MKCoordinateRegion是一个用来表示区域的结构体,定义如下
typedef struct { CLLocationCoordinate2D center; // 区域的中心点位置 MKCoordinateSpan span; // 区域的跨度 } MKCoordinateRegion; MKCoordinateSpan的定义 typedef struct { CLLocationDegrees latitudeDelta; // 纬度跨度 CLLocationDegrees longitudeDelta; // 经度跨度 } MKCoordinateSpan;
-
-
- 使用注意
- 设置对应的属性时,注意该属性是从哪个系统版本开始引入的,做好不同系统版本的适配
- 设置对应的属性时,一定要看清楚该属性只针对哪类地图是有效的
显示用户的位置
- 方式一: 显示用户的位置
- 设置MKMapView的
showsUserLocation
属性可以显示用户的当前位置 - 下图是显示效果
- 蓝色发光圆点就是用户的当前位置
- 蓝色发光圆点,专业术语叫做“大头针”
- 注意:iOS8.0之后,显示用户位置需要用户进行定位授权
- 导入CoreLocation框架
- 创建CLLocationManager,然后使用它请求用户授权
- 特点:
- 显示一个蓝点,在地图上标示用户的位置信息
- 不会自动放大地图,并且当用户位置移动时,地图不会自动跟着跑
- 设置MKMapView的
- 方式二: 追踪用户的位置(常用)
- 设置MKMapView的userTrackingMode属性可以跟踪显示用户的当前位置
- 特点:(与上面对比)
- 显示一个蓝点,在地图上标示用户的位置信息
- 会自动放大地图,并且当用户位置移动时,地图会自动跟着跑
- 注意:iOS8.0之后,显示用户位置需要用户进行定位授权
- 备注:
- iOS8.0-,地图不会自动滚动到用户所在位置
- iOS8.0+,地图会自动放大到合适比例,并显示出用户位置
MKMapView的代理
- MKMapView可以设置一个代理对象,用来监听地图的相关行为
-
常见的代理方法有
//一个位置更改默认只会调用一次,不断监测用户的当前位置 //每次调用,都会把用户的最新位置(userLocation参数)传进来,userLocation表示地图上用户位置那颗大头针 - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation; //地图的显示区域即将发生改变的时候调用 - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated; //地图的显示区域已经发生改变的时候调用 - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
MKUserLocation类:
- 这个类是系统自带的大头针模型类,就是用户位置显示的那颗大头针
- 一个类只要遵守了
协议就是一个大头针类型,该类就是遵守了这个协议 -
包括以下属性
//显示在大头针上的标题 @property (nonatomic, copy) NSString *title; //显示在大头针上的子标题 @property (nonatomic, copy) NSString *subtitle; //地理位置信息(大头针钉在什么地方?) @property (readonly, nonatomic) CLLocation *location;
代码举例:
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (nonatomic,strong) CLLocationManager *locationManager;
@end
@implementation ViewController
-(CLLocationManager *)locationManager{
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
}
return _locationManager ;
}
- (void)viewDidLoad {
[super viewDidLoad];
//请求定位iOS8.0+
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
//设置地图的类型
self.mapView.mapType = MKMapTypeStandard;
// 显示用户的位置
//方式一: 只显示不会追踪
// self.mapView.showsUserLocation = YES;
//方式二: 跟踪用户位置
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
//设置地图代理
self.mapView.delegate = self;
}
#pragma mark -MKMapViewDelegate
/*
当用户的位置更新,就会调用(不断地监控用户的位置,调用频率特别高)
userLocation 表示地图上蓝色那颗大头针的数据
*/
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
//1.给userLocation设置数据
userLocation.title = @"我是主标题";
userLocation.subtitle = @"我是子标题";
//2. 设置显示地图的中心点(将当前用户的位置设置为地图的中心点)
[mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
//3. 设置地图的展示范围
//我们如何拿到这个范围呢: 1. 实现下面的代理方法. 2. 拖动地图放大,一直放到认为合适的位置 3 . 在下面代理方法中,拿到相应的span
MKCoordinateSpan span = MKCoordinateSpanMake(0.021321, 0.019366);
MKCoordinateRegion region = MKCoordinateRegionMake(userLocation.location.coordinate, span);
[mapView setRegion:region animated:YES];
}
/*
用户缩放时,就会调用,该方法可用于调试出适合的跨度,然后用到setRegion方法中
*/
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
NSLog(@"%f %f", mapView.region.span.latitudeDelta, mapView.region.span.longitudeDelta);
}
@end
大头针
- 什么是大头针?
- 钉在某个具体位置,用来标识这个位置上有特定的事物(比如这个位置上有家餐馆)
大头针的基本使用
-
常用的方法(以下都是MapView的方法)
//添加一个大头针 - (void)addAnnotation:(id <MKAnnotation>)annotation; //添加多个大头针 - (void)addAnnotations:(NSArray *)annotations; //移除一个大头针 - (void)removeAnnotation:(id <MKAnnotation>)annotation; //移除多个大头针 - (void)removeAnnotations:(NSArray *)annotations;
(id <MKAnnotation>)annotation
参数是什么东西?- 大头针模型对象:用来封装大头针的数据,比如大头针的位置、标题、子标题等数据
- 只要任意一个对象遵守了
<MKAnnotation>
协议,并且实现协议中的@required
,则就是大头针模型对象 - 当调用了(添加大头针方法)时,系统就会自动创建一个大头针视图,内部数据由该模型对象决定
- 自定义大头针模型
- 为何要自定义大头针模型? 为何不使用系统自动带的大头针模型(MKUserLocation)呢?
- 因为系统自带的大头针MKUserLocation类的location属性为readonly,不能修改
- 当添加一个大头针时,位置肯定是不定的,因此需要自定义大头针模型
-
代码如下:
//1. 自定义大头针模型 #import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface ZHAnnotation : NSObject<MKAnnotation> /** 坐标位置 */ @property (nonatomic,assign) CLLocationCoordinate2D coordinate; /** 标题 */ @property (nonatomic, copy) NSString *title; /** 子标题 */ @property (nonatomic, copy) NSString *subtitle; @end #import "ZHAnnotation.h" @implementation ZHAnnotation @end //2. 添加大头针 #import "ViewController.h" #import <MapKit/MapKit.h> #import <CoreLocation/CoreLocation.h> #import "ZHAnnotation.h" @interface ViewController ()<MKMapViewDelegate> @property (weak, nonatomic) IBOutlet MKMapView *mapView; @property (nonatomic,strong) CLLocationManager *locationManager; @end @implementation ViewController -(CLLocationManager *)locationManager{ if (_locationManager == nil) { _locationManager = [[CLLocationManager alloc] init]; } return _locationManager ; } - (void)viewDidLoad { [super viewDidLoad]; //请求定位iOS8.0+ [self.locationManager requestWhenInUseAuthorization]; [self.locationManager requestAlwaysAuthorization]; //设置地图的类型 self.mapView.mapType = MKMapTypeStandard; //1. 跟踪用户位置 self.mapView.userTrackingMode = MKUserTrackingModeFollow; //2. 设置地图代理 self.mapView.delegate = self; // 3. 监听mapView的点击事件 [self.mapView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapMapView:)]]; } /** * 监听mapView的点击 */ - (void)tapMapView:(UITapGestureRecognizer *)tap{ // 1.获得用户在mapView点击的位置(x,y) CGPoint point = [tap locationInView:tap.view]; // 2.将数学坐标 转为 地理经纬度坐标 CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView]; // 3.创建大头针模型,添加大头针到地图上 //创建大头针 ZHAnnotation *anno = [[ZHAnnotation alloc] init]; anno.title = @"自定义主标题"; anno.subtitle = @"自定义子标题"; anno.coordinate = coordinate; [self.mapView addAnnotation:anno]; //随机大量添加 //中国的经纬度范围 // 纬度范围:N 3°51′ ~ N 53°33′ // 经度范围:E 73°33′ ~ E 135°05′ // for (int i = 0; i<1000; i++) { // ZHAnnotation *anno = [[ZHAnnotation alloc] init]; // CLLocationDegrees latitude = 4 + arc4random_uniform(50); // CLLocationDegrees longitude = 73 + arc4random_uniform(60); // anno.coordinate = CLLocationCoordinate2DMake(latitude, longitude); // anno.title = @"自定义主标题"; // anno.subtitle = @"自定义子标题"; // [self.mapView addAnnotation:anno]; // } } pragma mark -MKMapViewDelegate /* 当用户的位置更新,就会调用(不断地监控用户的位置,调用频率特别高) userLocation 表示地图上蓝色那颗大头针的数据 */ - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{ //1.给userLocation设置数据 userLocation.title = @"我是主标题"; userLocation.subtitle = @"我是子标题"; //2. 设置显示地图的中心点(将当前用户的位置设置为地图的中心点) [mapView setCenterCoordinate:userLocation.location.coordinate animated:YES]; //3. 设置地图的展示范围 MKCoordinateSpan span = MKCoordinateSpanMake(0.021321, 0.019366); MKCoordinateRegion region = MKCoordinateRegionMake(userLocation.location.coordinate, span); [mapView setRegion:region animated:YES]; }
- 为何要自定义大头针模型? 为何不使用系统自动带的大头针模型(MKUserLocation)呢?
自定义大头针
- 上面只是自定义了大头针显示的模型数据,并没有自定义大头针的显示样式
- 如何自定义大头针显示视图样式?
- 设置
MKMapView
的代理 -
实现MKMapView下面的代理方法,返回大头针控件
//根据传进来的(id <MKAnnotation>)annotation参数创建并返回对应的大头针视图控件 //根据模型来创建view返回出去 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;
- 调用时刻:
- 添加一个大头针或者显示用户的位置(用户显示在地图上时,那个蓝点也是一个大头针,并且是
(MKUserLocation *)userLocation
系统自带的大头针模型) - 即:只要地图上一有大头针,就会调用该代理方法,并把该大头针模型原型传递进来
- 添加一个大头针或者显示用户的位置(用户显示在地图上时,那个蓝点也是一个大头针,并且是
- 如果返回nil,显示出来的大头针就采取系统的默认样式
- 标识用户位置的蓝色发光圆点,它也是一个大头针,当显示这个大头针时,也会调用代理方法
- 因此,需要在代理方法中分清楚(id
)annotation参数代表自定义的大头针还是蓝色发光圆点 MKAnnotationView
方法返回值- 大头针视图控件
- 就是大头针那个View
- 调用时刻:
- 设置
MKAnnotationView类
- 地图上的大头针控件是MKAnnotationView
-
MKAnnotationView的属性
//大头针模型(当大头针显示时,会到该模型中取数据) @property (nonatomic, strong) id <MKAnnotation> annotation; //显示的图片(将大头针以图片的形式展示,若不设默认系统样式) @property (nonatomic, strong) UIImage *image; //是否显示标注(注意: 大头针一旦自定义,默认为NO) @property (nonatomic) BOOL canShowCallout; //标注的偏移量 @property (nonatomic) CGPoint calloutOffset; //标注右边显示什么控件 @property (strong, nonatomic) UIView *rightCalloutAccessoryView; //标注左边显示什么控件 @property (strong, nonatomic) UIView *leftCalloutAccessoryView; //标注下面显示什么控件(iOS9.0) @property (nonatomic, strong) UIView *detailCalloutAccessoryView
MKPinAnnotationView
- MKPinAnnotationView是MKAnnotationView的子类
-
MKPinAnnotationView比MKAnnotationView多了2个属性
//大头针颜色 @property (nonatomic) MKPinAnnotationColor pinColor; //大头针第一次显示时是否从天而降 @property (nonatomic) BOOL animatesDrop;
- 注意: 如果使用了该子类,父类的image属性无效
代码举例:
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "ZHAnnotation.h"
@interface ViewController ()<MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (nonatomic,strong) CLLocationManager *locationManager;
@end
@implementation ViewController
-(CLLocationManager *)locationManager{
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
}
return _locationManager ;
}
- (void)viewDidLoad {
[super viewDidLoad];
//请求定位iOS8.0+
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
//设置地图代理
self.mapView.delegate = self;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建大头针
//餐厅模型
ZHAnnotation *anno = [[ZHAnnotation alloc] init];
anno.title = @"饭店";
anno.subtitle = @"饭店子标题";
//给该模型添加一个icon属性!!!!
anno.icon = @"category_1";
anno.coordinate = CLLocationCoordinate2DMake(39, 115);
[self.mapView addAnnotation:anno];
//电影
ZHAnnotation *anno2 = [[ZHAnnotation alloc] init];
anno2.title = @"影院";
anno2.subtitle = @"影院子标题";
anno2.icon = @"category_5";
anno2.coordinate = CLLocationCoordinate2DMake(39, 116);
[self.mapView addAnnotation:anno2];
}
#pragma mark -MKMapViewDelegate
/*
该方法中大头针也是循环利用的
*/
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
//用户位置这个大头针不是ZHAnnotation 而是(MKUserLocation *)userLocation
if (![annotation isKindOfClass:[ZHAnnotation class]]) return nil;
static NSString *ID = @"tuangou";
// 从缓存池中取出可以循环利用的大头针view
MKAnnotationView *annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:ID];
if (annoView == nil) {
annoView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
//点击大头针,显示标题和子标题
annoView.canShowCallout = YES;
//设置大头针的偏移量
annoView.calloutOffset = CGPointMake(0, -10);
//设置大头针描述右边的内容
annoView.rightCalloutAccessoryView = [UISwitch new];
//设置大头针描述左边的内容
annoView.leftCalloutAccessoryView = [UISwitch new];
}
// 传递模型
annoView.annotation = annotation;
//设置图片
ZHAnnotation *anno = annotation;
annoView.image = [UIImage imageNamed:anno.icon];
return annoView;
}
// MKPinAnnotationView的使用
//- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
//{
// if (![annotation isKindOfClass:[ZHAnnotation class]]) return nil;
//
// static NSString *ID = @"tuangou";
// // 从缓存池中取出可以循环利用的大头针view
// MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID];
// if (annoView == nil) {
// annoView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
// annoView.canShowCallout = YES;
// // 设置从天而降的动画
// annoView.animatesDrop = YES;
// }
//
// // 传递模型
// annoView.annotation = annotation;
//
// return annoView;
//}
@end