MKUserLocation点的iOS 10的标题箭头
iOS 10中的地图应用程序现在在MKUserLocation
MKAnnotationView
顶部包含一个标题方向箭头。 有什么方法可以在我自己的应用程序中添加这个MKMapView
?
编辑:我会很高兴做到这一点手动,但我不知道是否有可能? 我可以在地图上添加一个注释并让它跟随用户的位置,包括animation移动吗?
我也遇到过这个相同的问题(需要一个方向指示器,而不会让地图旋转,类似于苹果地图应用程序)。 不幸的是,苹果还没有提供“标题”API的蓝色图标。
我创build了以下来自@ alku83实现的解决scheme。
- 确保类符合MKViewDelegate
-
添加委托方法以将蓝色箭头图标添加到地图位置点
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) { if views.last?.annotation is MKUserLocation { addHeadingView(toAnnotationView: views.last!) } }
-
添加方法来创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 } }
-
添加
var headingImageView: UIImageView?
到你们class上 这主要是转换/旋转蓝色箭头图像所需要的。 -
(根据您的架构,在不同的类/对象中)创build一个位置pipe理器实例,该类符合
CLLocationManagerDelegate
协议lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() // Set up your manager properties here manager.delegate = self return manager }()
-
确保您的位置pipe理器正在跟踪用户标题数据
locationManager.startUpdatingHeading()
并在适当的时候停止跟踪locationManager.stopUpdatingHeading()
-
添加
var userHeading: CLLocationDirection?
这将保持方向值 -
添加标题值更改时要通知的委托方法,并适当更改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) }
-
现在在符合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
值更新已经在MKPointAnnotation
, MKAnnotationView
和MKMapView
类中为我们实现了,所以我们不必自己去做。
我通过向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 }