Flecha de título de iOS 10 para el punto MKUserLocation

La aplicación Maps en iOS 10 ahora incluye una flecha de dirección de título en la parte superior de MKUserLocation MKAnnotationView . ¿Hay alguna forma de agregar esto a MKMapView en mis propias aplicaciones?

introduzca la descripción de la imagen aquí

Edit: Me encantaría hacer esto manualmente, pero no estoy seguro de si es posible. ¿Puedo agregar una anotación al map y hacer que siga la location del usuario, incluidos los movimientos animados?

    También experimenté este mismo problema (necesitaba un indicador de orientación sin girar el map, similar a la aplicación Apple Maps). Desafortunadamente, Apple aún no ha hecho disponible el API de "icono azul para encabezado".

    Creé la siguiente solución derivada de la implementación de @ alku83.

    1. Asegúrese de que la class se ajusta a MKViewDelegate
    2. Agregue el método delegado para agregar un icono de flecha azul al punto de location de los maps.

       func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) { if views.last?.annotation is MKUserLocation { addHeadingView(toAnnotationView: views.last!) } } 
    3. Agregue el método para crear el "icono de flecha azul".

       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. Agregar var headingImageView: UIImageView? a tu class Esto se necesita principalmente para transformar / rotar la image de flecha azul.

    5. (En una class / object diferente según su architecture) Cree una instancia de administrador de location, con la class conforme al protocolo CLLocationManagerDelegate

       lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() // Set up your manager properties here manager.delegate = self return manager }() 
    6. Asegúrese de que su administrador de location esté rastreando los datos de encabezado de usuario locationManager.startUpdatingHeading() y que deje de rastrear cuando sea apropiado. locationManager.stopUpdatingHeading()

    7. Agregue var userHeading: CLLocationDirection? que mantendrá el valor de orientación

    8. Agregue el método de delegado para recibir una notificación de cuándo cambian los valores del encabezado, y cambie el valor de userHeading apropiadamente

       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. Ahora en su class conforme a MKMapViewDelegate, agregue el método para 'transformar' la orientación de la image del título

        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) } } 

    Sí, puede hacerlo manualmente.

    La idea básica es rastrear la location del usuario con CLLocationManager y usar sus datos para colocar y rotar la vista de anotación en el map.

    Aquí está el código. Estoy omitiendo ciertas cosas que no están directamente relacionadas con la pregunta (por ejemplo, supongo que el usuario ya ha autorizado su aplicación para acceder a la location, etc.), por lo que probablemente desee modificar este código un poco

    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.desinetworkingAccuracy = 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) } } } 

    Aquí estamos haciendo la configuration básica de la vista del map y comenzando a rastrear la location y el rumbo del usuario con 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 } 

    Muy simple subclass MKPointAnnotation que es capaz de almacenar la dirección del rumbo. dynamic palabra key dynamic es la key aquí. Nos permite observar cambios en la propiedad del heading con KVO.

    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() } requinetworking 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") } } 

    Subclass MKAnnotationView que hace la observación de la propiedad de heading y luego establece la transformación de rotation apropiada en su subvista (en mi caso es solo una image con la flecha. Puede crear una vista de anotación más sofisticada y rotar solo una parte de ella en lugar de la totalidad ver.)

    UIView.animate es opcional. Se agrega para que la rotation sea más suave. CLLocationManager no es capaz de observar el valor del título 60 veces por segundo, por lo que al girar rápido, la animation puede ser un poco entrecortada. UIView.animate llamada UIView.animate resuelve este pequeño problema.

    El manejo adecuado de las actualizaciones de valores de coordinate ya está implementado en las MKPointAnnotation , MKAnnotationView y MKMapView para nosotros, por lo que no tenemos que hacerlo nosotros mismos.

    MKUserLocation esto al agregar una subvista a la anotación de annotations MKUserLocation , como así

     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 requinetworking }