iOS学习笔记 地图(二)MapKit框架
2016-09-20 19:09
274 查看
一、地图开发介绍
从iOS6.0开始地图数据不再由谷歌驱动,而是改用自家地图,当然在国内它的数据是由高德地图提供的。在iOS中进行地图开发主要有三种方式:
利用
MapKit框架进行地图开发,利用这种方式可以对地图进行精准的控制
调用苹果官方自带的地图应用,主要用于一些简单的地图应用,无法精确控制
使用第三方地图开发SDK库
用得最多的还是
MapKit,所以这节就只讲
MapKit的使用。
二、MapKit核心类
MapKit的核心类为地图展示控件
MKMapView,以下是常用的属性、对象方法以及代理方法。
1. 属性:
/* 用户位置跟踪 */ @property (nonatomic) BOOL showsUserLocation;/*< 是否在地图上标注用户位置 */ @property (nonatomic, readonly) MKUserLocation *userLocation;/*< 用户位置 */ @property (nonatomic) MKUserTrackingMode userTrackingMode;/*< 用户跟踪类型 */ typedef NS_ENUM(NSInteger, MKUserTrackingMode) { MKUserTrackingModeNone = 0, /*< 不跟踪 */ MKUserTrackingModeFollow, /*< 跟踪 */ MKUserTrackingModeFollowWithHeading, /*< 导航跟踪 */ }; /* 设置地图配置项 */ @property (nonatomic) MKMapType mapType;/*< 地图类型 */ @property (nonatomic, readonly) NSArray *annotations;/*< 大头针数组 */ typedef NS_ENUM(NSUInteger, MKMapType) { MKMapTypeStandard = 0,/*< 标准地图 */ MKMapTypeSatellite,/*< 卫星地图 */ MKMapTypeHybrid,/*< 混合模式(标准+卫星) */ MKMapTypeSatelliteFlyover,/*< 3D立体卫星(iOS9.0) */ MKMapTypeHybridFlyover,/*< 3D立体混合(iOS9.0) */ } /* 设置地图控制项 */ @property (nonatomic) BOOL zoomEnabled;/*< 是否可以缩放 */ @property (nonatomic) BOOL scrollEnabled;/*< 是否可以滚动 */ @property (nonatomic) BOOL rotateEnabled;/*< 是否可以旋转 */ @property (nonatomic) BOOL pitchEnabled;/*< 是否显示3D视角 */ /* 设置地图显示项 */ @property (nonatomic) BOOL showsBuildings;/*< 是否显示建筑物,只影响标准地图 */ @property (nonatomic) BOOL showsTraffic;/*< 是否显示交通,iOS9 */ @property (nonatomic) BOOL showsCompass;/*< 是否显示指南针,iOS9 */ @property (nonatomic) BOOL showsScale;/*< 是否显示比例尺,iOS9 */
所谓大头针就是地图上显示的这个标注:
大头针图片
2. 对象方法:
/* 添加大头针 */ - (void)addAnnotation:(id <MKAnnotation>)annotation; - (void)addAnnotations:(NSArray<id<MKAnnotation>> *)annotations; /* 删除大头针 */ - (void)removeAnnotation:(id <MKAnnotation>)annotation; - (void)removeAnnotations:(NSArray<id<MKAnnotation>> *)annotations; /* 选中大头针与取消选中大头针 */ - (void)selectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated; - (void)deselectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated; /* 获取大头针视图 */ - (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)annotation; /* 从缓冲池中取出大头针视图控件 */ - (MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier; /* 设置显示区域以及地图中心坐标 */ - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated; - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated; /* 经纬度坐标转UIKit坐标,UIKit坐标转经纬度坐标 */ - (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view; - (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view;
3. 常用代理方法MKMapViewDelegate
:
/* 地图加载完成会调用 */ - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView; /* 地图加载失败会调用 */ - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error; /* 用户位置发生改变会调用 */ - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation; /* 显示区域改变会调用 */ - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated; /* 点击选中大头针时会调用 */ - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view; /* 取消选中大头针时会调用 */ - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view; /* 显示地图上的大头针,功能类似于UITableView的tableView:cellForRowAtIndexPath:方法 */ - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation;
三、MapKit使用
1. 首先添加头文件:
#import <MapKit/MapKit.h>
2. 初始化地图展示控件MKMapView
- (void)initMapView{ CGFloat x = 0; CGFloat y = 20; CGFloat width = self.view.frame.size.width; CGFloat height = self.view.frame.size.height; //创建MKMapView,设置控件视图大小 MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(x, y, width, height)]; //设置地图类型 mapView.mapType = MKMapTypeStandard; //设置代理 mapView.delegate = self; [self.view addSubview:mapView]; self.mapView = mapView; }
3. 用户位置跟踪
在iOS8之前,实现这个功能只需要:设置用户跟踪模式
在
mapView:DidUpdateUserLocation:代理方法中设置地图中心和显示范围
在iOS8之后,用法稍有不同:
必须按照前面的定位章节的,获取前台或者前后台的定位服务授权,下面是链接:
iOS学习笔记19-地图(一)定位CoreLocation
不需要进行中心点的指定,默认会将当前位置设置为中心点并自动显示区域范围
只有定位到当前位置后
mapView:DidUpdateUserLocation:代理方法才会调用
- (void)viewDidLoad { [super viewDidLoad]; //获取定位服务授权 [self requestUserLocationAuthor]; //初始化MKMapView [self initMapView]; } - (void)requestUserLocationAuthor{ //如果没有获得定位授权,获取定位授权请求 self.locationM = [[CLLocationManager alloc] init]; if ([CLLocationManager locationServicesEnabled]) { if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) { [self.locationM requestWhenInUseAuthorization]; } } } - (void)initMapView{ CGFloat x = 0; CGFloat y = 20; CGFloat width = self.view.frame.size.width; CGFloat height = self.view.frame.size.height; //创建MKMapView对象 MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(x, y, width, height)]; //设置地图类型 mapView.mapType = MKMapTypeStandard; //设置用户跟踪模式 mapView.userTrackingMode = MKUserTrackingModeFollow; mapView.delegate = self; [self.view addSubview:mapView]; self.mapView = mapView; } #pragma mark - MKMapViewDelegate /* 更新用户位置会调用 */ - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{ CLLocation *location = userLocation.location; CLLocationCoordinate2D coordinate = location.coordinate; NSLog(@"经度:%f,纬度:%f",coordinate.latitude,coordinate.longitude); }
用户位置跟踪
4. 添加大头针
MapKit没有自带的大头针,只有大头针协议MKAnnotation,我们需要自定义大头针:
创建一个继承
NSObject的类
实现
MKAnnotation协议
必须创建一个属性,用于存储大头针位置
@property (nonatomic) CLLocationCoordinate2D coordinate;
下面就是我简单创建的LTAnnotation类:
#import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface LTAnnotation : NSObject <MKAnnotation> /* 必须创建的属性 */ @property (nonatomic) CLLocationCoordinate2D coordinate; /* 可选的属性 */ @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *subtitle; /* 自定义的属性 */ @property (nonatomic, strong) UIImage *icon; @end @implementation LTAnnotation @end
下面是实际的使用:
- (void)viewDidLoad { [super viewDidLoad]; //请求定位授权 [self requestUserLocationAuthor]; //初始化MKMapView [self initMapView]; //添加大头针 [self addAnnotationsToMapView]; } - (void)addAnnotationsToMapView{ CLLocationCoordinate2D location1 = CLLocationCoordinate2DMake(22.54, 114.02); //创建大头针 LTAnnotation *annotation = [[LTAnnotation alloc] init]; annotation.title = @"执着"; annotation.subtitle = @"执着哥开的店"; annotation.coordinate = location1; annotation.icon = [UIImage imageNamed:@"red"]; //添加大头针 [self.mapView addAnnotation:annotation1]; }
大头针在地图上的显示
点击大头针显示
5. 自定义大头针视图
上面的大头针样子是不是很丑,那是MKMapView的默认样式大头针视图
MKAnnotationView,我们先来了解下它的常用属性:
@property (nonatomic, strong) id<MKAnnotation> annotation;/*< 大头针数据 */ @property (nonatomic, strong) UIImage *image;/*< 大头针的图标 */ @property (nonatomic, readonly) NSString *reuseIdentifier;/*< 大头针的唯一标示 */ @property (nonatomic) CGPoint calloutOffset;/*< 弹出视图的偏移 */ @property (nonatomic) BOOL selected;/*< 是否选中 */ @property (nonatomic) BOOL canShowCallout;/*< 是否能点击弹出视图 */ @property (nonatomic, strong) UIView *leftCalloutAccessoryView;/*< 弹出视图左边的视图 */ @property (nonatomic, strong) UIView *rightCalloutAccessoryView;/*< 弹出视图右边的视图 */
下面是通过设置
MKAnnotationView的属性,自定义大头针视图:
/* 每当大头针显示在可视界面上时,就会调用该方法,用户位置的蓝色点也是个大头针,也会调用 */ - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { if ([annotation isKindOfClass:[LTAnnotation class]]) { LTAnnotation *annotationLT = (LTAnnotation *)annotation; //类似于UITableViewCell的重用机制,大头针视图也有重用机制 static NSString *key = @"AnnotationIdentifier"; MKAnnotationView *view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:key]; if (!view) { view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key]; } //设置大头针数据 view.annotation = annotation; //自定义大头针默认是NO,表示不能弹出视图,这里让大头针可以点击弹出视图 view.canShowCallout = YES; //设置大头针图标 view.image = annotationLT.icon; //设置弹出视图的左边视图 UIImage *leftImage = [UIImage imageNamed:@"cafeIcon"]; UIImageView *leftView = [[UIImageView alloc] initWithImage: leftImage]; leftView.bounds = CGRectMake(0, 0, 50, 50); view.leftCalloutAccessoryView = leftView; //设置弹出视图的右边视图 UIImage *rightImage = [UIImage imageNamed:@"cafeRight"]; UIImageView *rightView = [[UIImageView alloc] initWithImage: rightImage]; rightView.bounds = CGRectMake(0, 0, 50, 50); view.rightCalloutAccessoryView = rightView; return view; } //返回nil,表示显示默认样式 return nil; }
改变默认样式的大头针视图
四、扩展--自定义大头针弹出详情视图
如果你去关注下一些地图应用,会发现他们的弹出视图和我们的完全不一样,那是怎么实现的呢?实际上那不是弹出视图,那是个大头针,只是这个大头针做得和弹出视图很像而已。
实现思路:
当点击普通的大头针时,移除地图上其他的详情大头针,添加当前大头针的详情大头针
当普通大头针取消选中时,移除地图上所有的详情大头针
在
mapView:viewForAnnotation:方法中设置普通大头针视图和详情大头针视图
下面是实现的部分代码【实现效果比较随便,见谅】:
#pragma mark - 地图控件代理方法 /* 显示大头针时调用,注意方法中的annotation参数是即将显示的大头针对象 */ -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{ //由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图 if ([annotation isKindOfClass:[LTAnnotation class]]) { static NSString *key1 = @"AnnotationKey1"; MKAnnotationView *annotationView = [_mapView dequeueReusableAnnotationViewWithIdentifier:key1]; //如果缓存池中不存在则新建 if (!annotationView) { annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key1]; annotationView.canShowCallout = NO;//不允许弹出视图,但可以被选中 } //重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置) annotationView.annotation = annotation; annotationView.image = ((LTAnnotation *)annotation).icon;//设置大头针视图的图片 return annotationView; }else if([annotation isKindOfClass:[LTCalloutAnnotation class]]){ static NSString *key2 = @"AnnotationCallOutKey2"; MKAnnotationView *calloutView = [_mapView dequeueReusableAnnotationViewWithIdentifier:key2]; //如果缓存池中不存在则新建 if (!calloutView) { calloutView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key2]; calloutView.canShowCallout = NO;//不允许弹出视图,但可以被选中 } //对于作为弹出详情视图的自定义大头针视图无弹出交互功能,在其中可以自由添加其他视图 calloutView.annotation = annotation; //设置详情大头针的偏移位置 calloutView.centerOffset = CGPointMake(-50, -80); [self calloutAddSubView:calloutView]; return calloutView; } else { return nil; } }
上面我的LTCalloutAnnotation和LTAnnotation实际上是只是类名不同而已,属性都一样。
#pragma mark 添加弹出视图的子控件,这里我就很随便了,你可以搞得好看点 - (void)calloutAddSubView:(MKAnnotationView *)calloutView { //添加背景 UIView *background = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)]; background.backgroundColor = [UIColor whiteColor]; background.layer.borderWidth = 5; background.layer.borderColor = [UIColor blueColor].CGColor; [calloutView addSubview:background]; //添加图片 UIImage *image = [UIImage imageNamed:@"cafeRight"]; UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; imageView.frame = CGRectMake(5, 5, 50, 50); [calloutView addSubview:imageView]; //添加一个红色方块 UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(60, 5, 35, 40)]; subview.backgroundColor = [UIColor redColor]; [calloutView addSubview:subview]; } #pragma mark 选中大头针时触发 //点击一般的大头针KCAnnotation时添加一个大头针作为所点大头针的弹出详情视图 -(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{ if ([view.annotation isKindOfClass:[LTAnnotation class]]) { LTAnnotation *annotation = view.annotation; //点击一个大头针时移除其他弹出详情视图 [self removeCalloutAnnotation]; //添加详情大头针 LTCalloutAnnotation *callout = [[LTCalloutAnnotation alloc] init]; callout.icon = annotation.icon; callout.title = annotation.title; callout.subtitle = annotation.subtitle; callout.coordinate = annotation.coordinate; [self.mapView addAnnotation:callout]; } } #pragma mark 取消选中时触发 -(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{ [self removeCalloutAnnotation]; } #pragma mark 移除所用详情大头针 -(void)removeCalloutAnnotation{ [self.mapView.annotations enumerateObjectsUsingBlock:^(id obj,NSUInteger idx,BOOL *stop){ if ([obj isKindOfClass:[LTCalloutAnnotation class]]) { [_mapView removeAnnotation:obj]; } }]; }
自定义弹出视图的效果图
这个自定义弹出详情视图,我做的比较简陋,我主要是为了好说明具体是怎么实现的,你可以把弹出界面做的好看点,顺便把一些大头针视图进行下封装,那一切就很完美了,O(∩_∩)O哈!这种实现是很低效的,每次都需要遍历所有的大头针,从中找到详情大头针,需要优化的地方很多,可以自己去想着优化。
相关文章推荐
- 开发者所需要知道的 iOS 10 SDK 新特性
- 兼容iOS 10 资料整理笔记
- iOS应用国际化
- iOS10 下相册访问崩溃权限设置的问题
- iOS 10、Xcode 8 遇到部分问题解决记录
- 极光推送
- iOS10推送通知适配
- ios label上显示特殊字符 % "
- # iOS 10 适配 # 适配刷新控件 以MJRefresh 为例
- 《iOS开发笔记—文件的压缩与解压》
- 升级Xcode8,以及IOS 10带来的一些问题
- localeconv()
- iOS10--消息通知的基本使用
- ios修改textField的placeholder的字体颜色和大小
- iOS 统计项目代码行数
- iOS图片裁剪scale问题
- iOS 开发 - Xcode8下self.view.layer不能addSublayer,报错
- ios高德地图流程
- __BEGIN_DECLS 和 __END_DECLS 的理解
- ios实现加载框