The Maps app in iOS 10 now includes a heading direction arrow on top of the MKUserLocation
MKAnnotationView
. Is there some way I can add this to MKMapView
in my own apps?
Edit: I'd be happy to do this manually, but I'm not sure if it's possible? Can I add an annotation to the map and have it follow the user's location, including animated moves?
I also experienced this same issue (needing an orientation indicator without having the map spin around, similar to the Apple Maps app). Unfortunately Apple has not yet made the 'blue icon for heading' API available.
I created the following solution derived from @alku83's implementation.
Add the delegate method to add a blue arrow icon to the maps location dot
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
if views.last?.annotation is MKUserLocation {
addHeadingView(toAnnotationView: views.last!)
}
}
Add the method to create the 'blue arrow icon'.
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
}
}
Add var headingImageView: UIImageView?
to your class. This is mainly needed to transform/rotate the blue arrow image.
(In a different class/object depending on your architecture) Create a location manager instance, with the class conforming to CLLocationManagerDelegate
protocol
lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
// Set up your manager properties here
manager.delegate = self
return manager
}()
Ensure your location manager is tracking user heading data locationManager.startUpdatingHeading()
and that it stops tracking when appropriate locationManager.stopUpdatingHeading()
Add var userHeading: CLLocationDirection?
which will hold the orientation value
Add the delegate method to be notified of when the heading values change, and change the userHeading value appropriately
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)
}
Now in your class conforming to MKMapViewDelegate, add the method to 'transform' the orientation of the heading image
func updateHeadingRotation() {
if let heading = # YOUR locationManager instance#,
let headingImageView = headingImageView {
headingImageView.isHidden = false
let rotation = CGFloat(heading/180 * Double.pi)
headingImageView.transform = CGAffineTransform(rotationAngle: rotation)
}
}