MapKit框架使用
一.地图的基本使用
1.简介
- MapKit:用于地图展示,例如大头针/路线/覆盖层展示等(着重界面展示)
- 使用步骤:
- 导入主头文件 MapKit/MapKit.h
- MapKit有一个比较重要的UI控件:MKMapView,专门用于地图显示
2.显示类型/地图控制/显示控制
- 设置地图类型
- 地图的样式可以手动设置,在iOS9之前有3种,iOS9之后增加了2种
- MKMapTypeStandard:普通地图
- MKMapTypeSatellite:卫星云图
- MKMapTypeHybrid:混合模式(普通地图覆盖于卫星云图之上)
- MKMapTypeSatelliteFlyover:3D立体卫星(iOS9)
- MKMapTypeHybridFlyover:3D立体混合(iOS9)
- 设置方式:
- self.mapView.mapType = MKMapTypeStandard;
- 设置地图控制项
- 地图的旋转/缩放/移动等操作行为都可以开启或关闭
- 设置方式
- 是否缩放:self.customMapView.zoomEnabled = YES;
- 是否滚动:self.customMapVie.scrollEnabled = YES;
- 是否旋转:self.customMapVie.rotateEnabled = YES;
- 是否显示3DView:self.customMapVie.pitchEnable = NO;
- 设置地图显示项
- 地图上的指南针/比例尺/建筑物/POI点都可以控制是否显示
- 设置方式
- 是否显示指南针:self.customMapVie.showsCompass = YES;
- 是否显示比例尺:self.customMapVie.showsScale = YES;
- 是否显示交通:self.customMapVie.showsTraffic = YES;
- 是否显示建筑物:self.customMapVie.showsBuildings = YES;
- 常见问题总结
- 地图加载不显示:
- 地图放大都是格子,禁止浏览:
- 地图运行起来app占用内存太大:
- 测试环境
- 加载地图数据需要联网
- Xcode版本根据测试选择不同版本(iOS9只能使用Xcode7调试)
- iOS版本根据测试选择不同版本(例如地图类型,在iOS9之后才有新增)
3.显示用户位置
- 显示用户位置
- 可以设置显示用户当前所在位置,以一个蓝点的形式呈现在地图上
- 注意:如果要显示用户位置,在iOS8之后,需要主动请求用户授权
- 设置方案1:
- 代码:self.customMapView.showUserLocation = YES;
- 效果:会在地图上显示一个蓝点,标识用户所在的位置,但是地图不会缩放,而且当用户位置移动时,地图不会跟随用户位置移动而移动
- 设置方案2:
- 代码:self.customMapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
- 效果:会在地图上显示一个蓝点,标识用户所在的位置,而且地图缩放到合适比例,显示用户位置,当用户位置移动时,地图会跟随用户位置移动而移动,但是有时候失效
- 用户位置不显示可能的原因
- 检查代码,是否有设置显示用户的位置,是否有进行请求位置授权
- 查看模拟器是否有位置信息
- 重置模拟器
二.模拟器追踪显示用户位置
1.调整地图显示中心
- 设置地图代理
- 实现代理方法
- mapView:didUpdateUserLocation:
- MKUserLocation大头针数据模型详解
- MKUserLocation:被称作"大头针(数据)模型"
- 本质就是一个数据模型,只不过此模型遵循了大头针要遵循的协议(MKAnnotation)
- 重要属性:
- location(CLLocation对象):用户当前所在位置信息
- title(NSString对象):大头针标注要显示的标题
- subtitle(NSString对象):大头针标注要显示的子标题
- 调整地图中心
- 确定地图中心经纬度坐标
- CLLocationCoordinate2D center = CLLocationCoordinate2DMake(xxx, xxx);
- 设置地图中心为给定的经纬度坐标
- [mapView setCenterCoordinate:center animated:YES];
2.调整地图显示区域
- 获取合适的区域
- 实现当地图区域发生改变时调用的代理方法,并调整地图区域到合适比例,并在对应的方法中,获取对应的跨度信息
- mapView:regionDidChangeAnimated:
- 创建一个区域(包含区域中心和区域跨度)
- CLLocationCoordinate2D center = CLLocationCoordinate2DMake(xxx, xxx);
- MKCoordinateSpan span = MKCoordinateSpanMake(0.1, 0.1)
- MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
- 设置地图显示区域
- [self.mapView setRegion:region animated:YES];
- MKCoordianteSpan跨度解释
- latitudeDelta:经度跨度,因为南北纬各90度,所以此值的范围是(0-180),此值表示整个地图视图的宽度,显示多大跨度
- longtitudeDelta:纬度跨度,因为东西经各180度,所以此值的范围是(0-360),此值表示整个地图视图的高度,显示多大跨度
- 注意:地图视图显示,不会更改地图的比例,会以地图视图高度或宽度较小的那个为基准,按比例调整
3.常见问题总结
- 地图上蓝点不显示
- 确定代码是否有误,是否显示用户位置
- 确定模拟器是否设置位置
- 可确定位置是否在当前地图显示区域
- 地图跨度设置后,最终显示的跨度和设置数值不一致
- 因为地球不是正方形的,随着用户的位置而移动,会自动修正地图跨度,保持地图不变形
- 测试环境
- 加载地图数据需要联网
- Xcode版本不限/iOS系统版本不限
三.大头针基本使用
1.添加/删除
- 理论支持
- 按照MVC设计模式的原则(在地图上操作大头针,实际上是控制大头针数据模型)
- 添加大头针就是添加大头针数据模型
- 删除大头针就是删除大头针数据模型
- 在地图上添加大头针视图
- 自定义大头针数据模型
- 创建继承自NSObject的数据模型CustomAnnotation
- 遵循大头针数据模型必须遵守MKAnnotation协议
- 注意将协议@property中的readonly去掉
- 创建大头针数据模型,并初始化参数
- CustomAnnotation *annotation = [[CustomAnnotation allco] init];
- annotation.coordinate = coordinate;
- annotation.title = @"喳喳";
- annotation.subtitle = @"木喳喳的夏天";
- 调用地图的添加大头针数据模型的方法
- [self.customMapView addAnnotation:annotation];
- 移除大头针视图(移除所有大头针)
- NSArray *annotations = self.customMapView.annotations;
- [self.customMapView removeAnnotations:annotations];
2.场景模拟
- 场景描述
- 手指/鼠标点击在地图哪个位置,就在对应的位置添加一个大头针,并在标注弹框中显示对应的城市和街道
- 实现步骤
- 获取触摸点在地图上对应的坐标
- 将坐标转换为经纬度
- 根据经纬度创建大头针数据模型,并添加在地图上
- 利用反地理编码,,获取该点对应额城市和街道名称,然后修改大头针数据模型
- 注意:设置弹框数据时,对应的大头针数据模型应有对应的占位数据(这样对应的UI才会生成,后面才能重新修改数据)
- 常见问题
- 反地理编码无法获取对应的数据
- 检查是否联网
- 检查代码是否有误
- 又是存在某些位置没有反地理编码结果,换个点尝试,如果都没有,排除此原因
- 大头针协议遵循以及属性设置
- @property其实就是生成get和set方法
- 所以,遵循这个协议等同于实现了该属性的set/get方法
四.自定义大头针
1.模拟系统实现方案
- 理论支持(依照MVC的原则)
- 每当添加一个大头针数据模型时,地图就会调用对应的代理方法,查找对应的大头针视图,显示在地图上
- 如果该方法没有实现,或者返回nil,那么就会使用系统默认的大头针视图
- 模拟系统默认的实现方案
- 实现当添加大头针数据模型时,地图回调的代理方法
- mapView:viewForAnnotation:
- 大头针对应的视图是MKPinAnnotationView,它继承自MKAnnotationView
- 地图上的大头针视图,和tableView的cell一样,都使用"循环利用"的机制
2.自定义大头针和弹框
- 实现当添加大头针数据模型时,地图回调的代理方法(mapView:viewForAnnotation)
- 如果想要自定义大头针,必须使用MKAnnotationView或者自定义的子类
- 但是不能直接使用系统默认的大头针,会无效
3.代理方法补充
- 选中一个大头针时调用:mapView:didSelectAnnotationView:
- 取消选中大头针时调用:mapView:didDeselectAnnotationView:
五.导航简介
- 概念:简单来说,就是根据用户指定的位置,进行路线规划,然后根据用户在行走过程中,实时给出指引提示
- 导航的三种实现方案:
- 可以将需要导航的位置发送给系统的地图APP进行导航
- 发送网络请求到公司服务器获取导航数据,然后自己手动绘制导航
- 利用第三方SDK实现导航(百度)
六.使用系统APP进行导航
1.利用"反推发",记住关键代码即可
- 根据MKMapItem数组和启动参数字典,来调用系统地图进行导航
- 代码:[MKMapItem openMapsWithIterms:@[item1,item2] launchOptions:launchDic];
- 注意:CLPlacemark地表对象没法直接手动创建,只能通过(反)地理编码获取
2.课题研究
- 3D视图
- 地图截图
- 截图附加选项
- MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
- 设置截图区域(在地图上的区域,作用在地图)
- options.region = self.mapView.region;
- 设置截图后的图片大小(作用在输出图像)
- options.size = self.mapView.frame.size;
- 设置截图后的图片比例(默认是屏幕比例,作用在输出图像)
- options.scale = [[UIScreen mainScreen] scale];
- 创建截图对象并开始截图
- MKMapSnapshotter *snapshotter = [[MKMapSnapShotter alloc] initWithOptions:options];
- [snapshotter startWithCompletionHandler:block];
- POI检索
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "小吃"
request.region = mapView.region
let searcher: MKLocalSearch = MKLocalSearch(request: request)
searcher.startWithCompletionHandler { (response: MKLocalSearchResponse?, error: NSError?) in
guard let items = response?.mapItems else { return }
for item in items {
print(item.name, item.phoneNumber, item.url)
}
}
七.发送网络请求给苹果服务器进行导航
1.发送网络请求给苹果服务器获取导航路线
- 实现须知
- 获取导航路线,需要向苹果服务器发送网络请求
- 记住关键对象MKDirections
- 代码实现
- 根据两个地标,向苹果服务器发送对应的行走路线信息
- 创建请求
- 设置开始地标
- 设置结束地标
- 根据请求,获取实际路线信息
- MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
- [directions calculateDirectionWithCompletionHandler:block];
- 测试环境
- 请求路线数据需要联网
- Xcode版本不限/iOS系统版本不限
2.解析导航数据
- 导航路线对象详解
- MKDirectionsResponse对象解析
- source:开始位置
- destination:结束位置
- routes:路线信息(MKRoute对象)
- MKRoute对象解析
- name:路的名称
- advisoryNotices:注意警告信息
- distance:路线长度(实际物理距离,单位是m)
- polyline:路线对应在地图上的几何线路数据模型(由很多点组成,可绘制在地图上)
- steps:多个行走步骤组成的数组(例如"前方路口左转","保持之行"等,MKRouteStep对象)
- MKRouteStep对象解析
- instruction:步骤说明(例如"前方路口左转","保持之行"等)
- transportType:交通方式(驾车,步行等)
- polyline:路线对应在地图上的几何线路数据模型(由很多点组成,可绘制在地图上)
- 注意:MKRoute是一整条长路,MKRouteStep是这条长路中的每一截
- 常见问题总结
- 类太多,记不住,只需要知道有这么一个功能即可,如果用到时,直接回头找代码即可.
3.绘制导航路线
- 理论支持
- 路线也是一个覆盖层
- 在地图上操作覆盖层,其实操作的是覆盖层的数据模型
- 添加覆盖层:在地图上添加覆盖层数据模型
- 删除覆盖层:在地图上移除覆盖层数据模型
- 添加导航路线到地图
- 获取几何路线的数据模型(id)overlay
- 在地图上添加覆盖层(几何路线也是一个覆盖层),直接添加覆盖层数据模型
- [self.mapView addOverlay:overlay];
- 设置地图代理,代理遵循MKMapViewDelegate协议
- 实现地图添加覆盖层数据模型时,回调的代理方法,通过此方法,返回对应的渲染图层
- mapView:renderForOverlay:
- 添加圆形覆盖层到地图
- 创建圆形区域覆盖层的数据模型
- 添加覆盖层数据模型
- 实现代理方法
- 测试环境
- 地图加载需要联网
- Xcode版本不限/iOS系统版本不限
- 常见问题总结
- 类太多,记不住怎么办?
- 只需要记住一个思想,按照MVC的原则,我们操作覆盖层,就是操作覆盖层数据模型
- 然后地图会调用其对应的代理方法,获取对应的覆盖层渲染层
八.使用第三方SDK进行导航
1.集成百度地图sdk
- 集成原因
- 有些功能,系统自带的高德地图无法实现,例如POI检索等
- 一般实现导航功能,集成百度地图的比较多
- 集成步骤
- 注册用户并登陆百度地图开放平台
- 根据需求,选择不同的sdk
- 下载对应的sdk
- 按照集成文档一步步实现
- 文档说明
- 创建应用,并获取秘钥(注意APP BundleID的配置要和项目工程一致)
- 参照集成文档,配置对应的开发环境
- 工程中至少有一个.mm后缀的源文件(一般修改代理文件的后缀为AppDelegate.mm)
- Other Linker Flags中添加"-Objc"(Swift与OC进行混编)
- iOS9.0ATS适配(将Allow Arbitrary Loads设置为YES)
- iOS8.0定位适配(设置NSLocationAlwaysUsageDescription为定位说明文字)
- 在info.plist中添加Bundle display name为"$(PRODUCT_NAME)"
- 添加系统的依赖库
- CoreLocation.framework
- QuartzCore.framework
- OpenGLES.framework
- SystemConfiguration.framework
- CoreGraphics.framework
- Security.framework
- libsqlite3.0.tbd(xcode7以前为 libsqlite3.0.dylib)、CoreTelephony.framework
- libstdc++.6.0.9.tbd(xcode7以前为libstdc++.6.0.9.dylib
- 引入mapapi.bundle资源文件(注意: 需要单独导入,放在.framework中,拖入不到工程)
- 根据功能需求,实现不同的功能代码
2.使用百度地图进行POI检索/添加大头针/集成百度导航SDK/定位及导航
3.代码封装重构
- 不要把所有的功能全部都写在控制器当中,最好封装成一个单独的工具类
- 如果集成过程中出现问题,先查看官方文档