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

    }
}

results matching ""

    No results matching ""