MapKit
要使用MapKit,首先要导入MapKit
import MapKit
MKMapView
设置地图的样式
mapView.mapType = .satellite
枚举 | 说明 |
---|---|
standard | 2D标准地图 |
satellite | 卫星地图 |
hybrid | 混合地图 |
satelliteFlyover | 3D立体卫星地图 |
hybridFlyover | 3D立体混合 |
设置地图的控制项
mapView.isScrollEnabled = false //地图是否支持滚动
mapView.isRotateEnabled = false //地图是否支持旋转
mapView.isZoomEnabled = false //地图是否支持缩放
设置地图的显示项
mapView.showsBuildings = true//显示建筑物
mapView.showsScale = true//显示比例尺
mapView.showsPointsOfInterest = true//显示兴趣点
mapView.showsTraffic = true//显示交通状况
mapView.showsCompass = true//显示指南针
显示用户位置
注意!要使用用户位置信息,需要先设置Privacy - Location Always Usage Description权限,然后
//懒加载一个CLLocationManager对象
lazy var locationM: CLLocationManager = {
let locationM = CLLocationManager()
if #available(iOS 8.0, *) {
locationM.requestAlwaysAuthorization()
}
return locationM
}()
//在使用位置信息之前先使用一次CLLocationManager对象
_ = locationM
//然后再开启用户位置信息
mapView.showsUserLocation = true
showsUserLocation的特点:
显示一个蓝点, 在地图上面标示用户的位置信息
但是, 不会自动放大地图, 并且当用户 位置移动时, 地图不会自动跟着跑
用户的追踪模式
显示一个蓝点, 在地图上面标示用户的位置信息
会自动放大地图, 并且当用户 位置移动时, 地图会自动跟着跑
缺点:不灵光
/*
case none // 不跟踪
case follow // 地图跟踪用户位置
case followWithHeading // 地图跟踪用户位置并且显示设备朝向
*/
mapView.userTrackingMode = .followWithHeading
如要手动设置蓝点为中心点,首先要设置MKMapView对象的代理 MKMapViewDelegate
并且实现
//当前地图更新用户位置信息时调用的方法
//蓝点:大头针视图,或是大头针数据模型
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
//MKUserLocation 大头针数据模型
//location:表示大头针位置信息(经纬度)
//heading:设备朝向
//title:弹框标题
//subtitle:弹框副标题
userLocation.title = "张帆"
userLocation.subtitle = "和小朵朵"
//移动地图的中心,显示在当前用户所在的位置
mapView.setCenter((userLocation.location?.coordinate)!, animated: true)
//如果想让地图自动切换到最大的显示跨度,可以做如下设置
//设置用户的中心点
let center = (userLocation.location?.coordinate)!
//设置地图显示的跨度
let span = MKCoordinateSpanMake(0.001, 0.001)
//设置显示区域
let region:MKCoordinateRegion = MKCoordinateRegionMake(center, span)
//移动地图
mapView.setRegion(region, animated: true)
}
MKCoordinateSpan 跨度:
- latitudeDelta:纬度跨度,因为南北纬各90.0度,所以此值的范围是(0.0---180.0);此值表示,整个地图视图宽度,显示多大跨度;
- longitudeDelta:经度跨度,因为东西经各180.0度,所以此值范围是(0.0---360.0):此值表示,整个地图视图高度,显示多大跨度;
注意:地图视图显示,不会更改地图的比例,会以地图视图高度或宽度较小的那个为基准,按比例调整
区域改变时的代理方法, 当地图区域改变的时候会调用
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
print(mapView.region.span.latitudeDelta,mapView.region.span.longitudeDelta)
}
常见问题总结
地图上的蓝点为啥不显示?
- 确定代码是否有误(例如, 是否显示了用户位置)
- 确定模拟器是否设置位置
- 看下位置在哪, 是不是不在当前地图显示区域
地图跨度设置之后, 最终显示的跨度和设置数值不一致?
- 因为地球的不是正方形的, 随着用户的位置移动, 会自动修正地图跨度, 保持地图不变形;
大头针的基本使用
理论支撑(必须掌握)
按照MVC的原则
- 在地图上操作大头针,实际上是控制大头针数据模型
- 添加大头针就是添加大头针数据模型
- 删除大头针就是删除大头针数据模型
在地图上添加大头针视图
自定义大头针数据模型
创建继承自NSObject的数据模型XMGAnnotation, 遵循大头针数据模型必须遵循的协议(MKAnnotation)
注意将协议@property 中的readonly 去掉;
import MapKit
class ZFAnnotation: NSObject, MKAnnotation {
// 确定大头针的位置
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(0.0, 0.0)
// 弹框的标题
var title: String?
// 弹框的子标题
var subtitle: String?
}
- 创建大头针数据模型, 并初始化参数
let annotation:ZFAnnotation = ZFAnnotation()
annotation.coordinate = mapView.centerCoordinate
annotation.title = "闪阁制造"
annotation.subtitle = "Flashloft"
- 调用地图的添加大头针数据模型方法
mapView.addAnnotation(annotation)
//添加一个数组中的所有大头针
//mapView.addAnnotations(annotations)
移除大头针
mapView.removeAnnotations(mapView.annotations)
根据手指在地图上触摸,创建大头针并显示大头针的位置信息
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//根据touch事件获取mapView上面触摸的点
let point = touches.first?.location(in: mapView)
//将获取到的触摸点转换为地理坐标
let coordinate = mapView.convert(point!, toCoordinateFrom: mapView)
//根据地理坐标创建大头针
let annotation = addAnnotation(coordinate: coordinate)
//根据地理坐标创建CLLocation对象
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
//获取反地理编码
geoCoder.reverseGeocodeLocation(location) { (placemarks: [CLPlacemark]?,error:Error?) in
if error == nil {
guard let placemark = placemarks!.first else {
return
}
//将获取到的地理信息的一些字段赋值给大头针标题
annotation.title = placemark.locality
annotation.subtitle = placemark.name
self.mapView.addAnnotation(annotation)
}
}
}
func addAnnotation(coordinate:CLLocationCoordinate2D) -> ZFAnnotation {
let annotation:ZFAnnotation = ZFAnnotation()
annotation.coordinate = coordinate
return annotation
}
自定义大头针
实现MKMapViewDelegate的代理方法,就和TableView的Cell类似
/**
如果当我们添加一个大头针数据模型, 到地图上, 那么地图就会自动调用一个代理方法, 来查找对应的大头针"视图!!!!"
- parameter mapView: 地图
- parameter annotation: 大头针"数据模型"
- returns: 大头针"视图"
// 注意事项: 如果这个方法没有实现, 或者返回Nil, 那么就会使用系统默认的大头针视图来显示
*/
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// 系统大头这视图对应的类 MKPinAnnotationView
// 大头针视图和cell一样, 都有一个"循环利用"机制
// 1. 从缓存池取出大头针视图
let annoId = "anno"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annoId) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: annoId)
}
//显示弹框
annotationView?.canShowCallout = true
//设置大头针颜色
annotationView?.pinTintColor = UIColor.green
//设置大头针落下动画
annotationView?.animatesDrop = true
print("anno")
return annotationView
}
完全修改大头针样式的自定义以及自定义弹框,可以使用 MKAnnotationView, 或者是自己定义的子类
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annoId = "anno"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annoId)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annoId)
}
//防止循环利用机制带来的数据错乱,关键的一步
annotationView?.annotation = annotation
//设置大头针的自定义图片
annotationView?.image = UIImage(named: "category_\(arc4random_uniform(5) + 1)")
//设置大头针中心的偏移量
annotationView?.centerOffset = CGPoint(x: 0, y: -20)
//显示弹框
annotationView?.canShowCallout = true
//设置弹框的偏移量
annotationView?.calloutOffset = CGPoint(x: 0, y: -10)
//设置弹框的左视图
let leftImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
leftImageView.image = UIImage(named: "htl")
leftImageView.layer.cornerRadius = 20
leftImageView.clipsToBounds = true
annotationView?.leftCalloutAccessoryView = leftImageView
//设置弹框的右视图
let rightImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
rightImageView.image = UIImage(named: "eason")
rightImageView.layer.cornerRadius = 20
rightImageView.clipsToBounds = true
annotationView?.rightCalloutAccessoryView = rightImageView
//设置弹框的底部视图
if #available(iOS 9.0, *){
annotationView?.detailCalloutAccessoryView = UISwitch()
}
//设置大头针可以拖动
annotationView?.isDraggable = true
return annotationView
}
}
一些其他长用代理方法
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
// 拿到数据模型
let annotation = view.annotation
print("选中了\(annotation?.title)")
}
func mapView(mapView: MKMapView, didDeselectAnnotationView view: MKAnnotationView) {
// 拿到数据模型
let annotation = view.annotation
print("取消选中了\(annotation?.title)")
}
创建追踪模式切换导航按钮 MKUserTrackingBarButtonItem
import MapKit
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
lazy var locationM: CLLocationManager = {
let locationM = CLLocationManager()
if #available(iOS 8.0, *) {
locationM.requestAlwaysAuthorization()
}
return locationM
}()
override func viewDidLoad() {
super.viewDidLoad()
_ = locationM
let item = MKUserTrackingBarButtonItem(mapView: mapView)
navigationItem.leftBarButtonItem = item
}
}