MKUserLocation点的iOS 10标题箭头

问题描述 投票:16回答:3

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

enter image description here

编辑:我很乐意手动执行此操作,但我不确定它是否可行?我可以在地图上添加注释并让它跟随用户的位置,包括动画移动吗?

ios mkmapview ios10 mkuserlocation
3个回答
12
投票

我也遇到了同样的问题(需要一个方向指示器,而不需要地图旋转,类似于Apple Maps应用程序)。不幸的是,Apple还没有提供'蓝色图标'标题'API。

我创建了以下@ alku83实现的解决方案。

  1. 确保该类符合MKViewDelegate
  2. 添加委托方法以将蓝色箭头图标添加到地图位置点 func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) { if views.last?.annotation is MKUserLocation { addHeadingView(toAnnotationView: views.last!) } }
  3. 添加方法以创建“蓝色箭头图标”。 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?添加到您的班级。这主要用于变换/旋转蓝色箭头图像。
  5. (在不同的类/对象中,具体取决于您的体系结构)创建一个位置管理器实例,该类符合CLLocationManagerDelegate协议 lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() // Set up your manager properties here manager.delegate = self return manager }()
  6. 确保您的位置管理员正在跟踪用户标题数据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 * Double.pi) headingImageView.transform = CGAffineTransform(rotationAngle: rotation) } }

5
投票

是的,您可以手动执行此操作。

基本思想是使用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属性,然后将适当的旋转变换设置为它的子视图(在我的例子中,它只是带箭头的图像。您可以创建更复杂的注释视图并仅旋转其中的一部分而不是整个视图。)

UIView.animate是可选的。添加它以使旋转更平滑。 CLLocationManager无法每秒观察60次航向值,因此当快速旋转时,动画可能会有点不稳定。 UIView.animate电话解决了这个小问题。

正确处理coordinate值更新已经在MKPointAnnotationMKAnnotationViewMKMapView类中为我们实现,因此我们不必自己完成。


2
投票

我通过在MKUserLocation annotationView中添加子视图来解决这个问题,就像这样

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
}
© www.soinside.com 2019 - 2024. All rights reserved.