MKUserLocation点的iOS 10的标题箭头

iOS 10中的地图应用程序现在在MKUserLocation MKAnnotationView顶部包含一个标题方向箭头。 有什么方法可以在我自己的应用程序中添加这个MKMapView

在这里输入图像说明

编辑:我会很高兴做到这一点手动,但我不知道是否有可能? 我可以在地图上添加一个注释并让它跟随用户的位置,包括animation移动吗?

我也遇到过这个相同的问题(需要一个方向指示器,而不会让地图旋转,类似于苹果地图应用程序)。 不幸的是,苹果还没有提供“标题”API的蓝色图标。

我创build了以下来自@ alku83实现的解决scheme。

  1. 确保类符合MKViewDelegate
  2. 添加委托方法以将蓝色箭头图标添加到地图位置点

     func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) { if views.last?.annotation is MKUserLocation { addHeadingView(toAnnotationView: views.last!) } } 
  3. 添加方法来创build“蓝色箭头图标”。

     func addHeadingView(toAnnotationView annotationView: MKAnnotationView) { if headingImageView == nil { let image = #YOUR BLUE ARROW ICON# headingImageView = UIImageView(image: image) headingImageView!.frame = CGRect(x: (annotationView.frame.size.width - image.size.width)/2, y: (annotationView.frame.size.height - image.size.height)/2, width: image.size.width, height: image.size.height) annotationView.insertSubview(headingImageView!, at: 0) headingImageView!.isHidden = true } } 
  4. 添加var headingImageView: UIImageView? 到你们class上 这主要是转换/旋转蓝色箭头图像所需要的。

  5. (根据您的架构,在不同的类/对象中)创build一个位置pipe理器实例,该类符合CLLocationManagerDelegate协议

     lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() // Set up your manager properties here manager.delegate = self return manager }() 
  6. 确保您的位置pipe理器正在跟踪用户标题数据locationManager.startUpdatingHeading()并在适当的时候停止跟踪locationManager.stopUpdatingHeading()

  7. 添加var userHeading: CLLocationDirection? 这将保持方向值

  8. 添加标题值更改时要通知的委托方法,并适当更改userHeading值

     func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { if newHeading.headingAccuracy < 0 { return } let heading = newHeading.trueHeading > 0 ? newHeading.trueHeading : newHeading.magneticHeading userHeading = heading NotificationCenter.default.post(name: Notification.Name(rawValue: #YOUR KEY#), object: self, userInfo: nil) } 
  9. 现在在符合MKMapViewDelegate的类中,添加方法来“转换”标题图像的方向

      func updateHeadingRotation() { if let heading = # YOUR locationManager instance#, let headingImageView = headingImageView { headingImageView.isHidden = false let rotation = CGFloat(heading/180 * M_PI) headingImageView.transform = CGAffineTransform(rotationAngle: rotation) } } 

是的,你可以手动做到这一点。

基本思想是用CLLocationManager跟踪用户的位置,并使用它的数据在地图上放置和旋转注解视图。

这是代码。 我忽略了某些与问题没有直接关系的东西(例如,我假设用户已经授权您的应用程序进行位置访问等),所以您可能需要稍微修改此代码

ViewController.swift

 import UIKit import MapKit class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate { @IBOutlet var mapView: MKMapView! lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() manager.delegate = self return manager }() var userLocationAnnotation: UserLocationAnnotation! override func viewDidLoad() { super.viewDidLoad() locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation locationManager.startUpdatingHeading() locationManager.startUpdatingLocation() userLocationAnnotation = UserLocationAnnotation(withCoordinate: CLLocationCoordinate2D(), heading: 0.0) mapView.addAnnotation(userLocationAnnotation) } func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { userLocationAnnotation.heading = newHeading.trueHeading } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { userLocationAnnotation.coordinate = locations.last!.coordinate } public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { if let annotation = annotation as? UserLocationAnnotation { let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "UserLocationAnnotationView") ?? UserLocationAnnotationView(annotation: annotation, reuseIdentifier: "UserLocationAnnotationView") return annotationView } else { return MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil) } } } 

在这里,我们正在做地图视图的基本设置,并开始用CLLocationManager跟踪用户的位置和标题。

UserLocationAnnotation.swift

 import UIKit import MapKit class UserLocationAnnotation: MKPointAnnotation { public init(withCoordinate coordinate: CLLocationCoordinate2D, heading: CLLocationDirection) { self.heading = heading super.init() self.coordinate = coordinate } dynamic public var heading: CLLocationDirection } 

非常简单的能够存储航向的MKPointAnnotation子类。 dynamic关键字是这里的关键。 它允许我们观察KVO对heading属性的更改。

UserLocationAnnotationView.swift

 import UIKit import MapKit class UserLocationAnnotationView: MKAnnotationView { var arrowImageView: UIImageView! private var kvoContext: UInt8 = 13 override public init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) arrowImageView = UIImageView(image: #imageLiteral(resourceName: "Black_Arrow_Up.svg")) addSubview(arrowImageView) setupObserver() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) arrowImageView = UIImageView(image: #imageLiteral(resourceName: "Black_Arrow_Up.svg")) addSubview(arrowImageView) setupObserver() } func setupObserver() { (annotation as? UserLocationAnnotation)?.addObserver(self, forKeyPath: "heading", options: [.initial, .new], context: &kvoContext) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if context == &kvoContext { let userLocationAnnotation = annotation as! UserLocationAnnotation UIView.animate(withDuration: 0.2, animations: { [unowned self] in self.arrowImageView.transform = CGAffineTransform(rotationAngle: CGFloat(userLocationAnnotation.heading / 180 * M_PI)) }) } } deinit { (annotation as? UserLocationAnnotation)?.removeObserver(self, forKeyPath: "heading") } } 

MKAnnotationView子类,用于观察heading属性,然后为其子视图设置适当的旋转变换(在我的情况下,它只是一个带有箭头的图像),您可以创build更复杂的注释视图并仅旋转其中的一部分而不是整个视图。)

UIView.animate是可选的。 它被添加使旋转更平滑。 CLLocationManager不能每秒钟观察标题值60次,所以快速旋转时,animation可能会有点波动。 UIView.animate调用解决了这个小问题。

正确处理coordinate值更新已经在MKPointAnnotationMKAnnotationViewMKMapView类中为我们实现了,所以我们不必自己去做。

我通过向MKUserLocation注解视图添加一个子视图来解决这个问题

 func mapView(mapView: MKMapView, didAddAnnotationViews views: [MKAnnotationView]) { if annotationView.annotation is MKUserLocation { addHeadingViewToAnnotationView(annotationView) } } func addHeadingViewToAnnotationView(annotationView: MKAnnotationView) { if headingImageView == nil { if let image = UIImage(named: "icon-location-heading-arrow") { let headingImageView = UIImageView() headingImageView.image = image headingImageView.frame = CGRectMake((annotationView.frame.size.width - image.size.width)/2, (annotationView.frame.size.height - image.size.height)/2, image.size.width, image.size.height) self.headingImageView = headingImageView } } headingImageView?.removeFromSuperview() if let headingImageView = headingImageView { annotationView.insertSubview(headingImageView, atIndex: 0) } //use CoreLocation to monitor heading here, and rotate headingImageView as required }