![](/img/trans.png)
[英]Is there a way to subclass the MKAnnotationView used for the MKUserLocation blue dot?
[英]iOS 10 heading arrow for MKUserLocation dot
我也遇到了同樣的問題(需要定位指示器而不會旋轉地圖,類似於Apple Maps應用程序)。 不幸的是,Apple尚未提供“藍色標題圖標” API。
我創建了以下源自@ alku83的實現的解決方案。
添加委托方法以將藍色箭頭圖標添加到地圖位置點
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) { if views.last?.annotation is MKUserLocation { addHeadingView(toAnnotationView: views.last!) } }
添加方法以創建“藍色箭頭圖標”。
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?
上你的課。 主要需要變換/旋轉藍色箭頭圖像。
(根據您的體系結構,在不同的類/對象中)創建一個位置管理器實例,該類符合CLLocationManagerDelegate
協議
lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() // Set up your manager properties here manager.delegate = self return manager }()
確保您的位置管理器正在跟蹤用戶標題數據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 * Double.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
屬性,然后將適當的旋轉變換設置為其子視圖(在我的情況下,它只是帶有箭頭的圖像。您可以創建更復雜的注釋視圖,並只旋轉其中一部分而不是整個視圖。)
UIView.animate
是可選的。 添加它可使旋轉更平滑。 CLLocationManager
無法每秒觀察60次航向值,因此在快速旋轉時,動畫可能會有點斷斷續續。 UIView.animate
調用解決了這個小問題。
MKPointAnnotation
, MKAnnotationView
和MKMapView
類已經為我們實現了coordinate
值更新的正確處理,因此我們不必自己做。
我通過向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
}
我不知道為什么沒人提供delegate
解決方案。 它不依賴MKUserLocation
,而是使用@Dim_ov提出的方法,即子類化MKPointAnnotation
和MKAnnotationView
(最MKAnnotationView
,最通用的IMHO方法)。 唯一的區別是觀察者現在已被delegate
方法替換。
創建delegate
協議:
protocol HeadingDelegate : AnyObject { func headingChanged(_ heading: CLLocationDirection) }
創建用於通知委托的MKPointAnnotation
子類。 headingDelegate
屬性將從視圖控制器外部分配,並在每次heading
屬性更改時觸發:
class Annotation : MKPointAnnotation { weak var headingDelegate: HeadingDelegate? var heading: CLLocationDirection { didSet { headingDelegate?.headingChanged(heading) } } init(_ coordinate: CLLocationCoordinate2D, _ heading: CLLocationDirection) { self.heading = heading super.init() self.coordinate = coordinate } }
創建實現委托的MKAnnotationView
子類:
class AnnotationView : MKAnnotationView , HeadingDelegate { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) } func headingChanged(_ heading: CLLocationDirection) { // For simplicity the affine transform is done on the view itself UIView.animate(withDuration: 0.1, animations: { [unowned self] in self.transform = CGAffineTransform(rotationAngle: CGFloat(heading / 180 * .pi)) }) } }
考慮到您的視圖控制器同時實現了CLLocationManagerDelegate
和MKMapViewDelegate
,剩下要做的很少(這里不提供完整的視圖控制器代碼):
// Delegate method of the CLLocationManager func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { userAnnotation.heading = newHeading.trueHeading } // Delegate method of the MKMapView func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: NSStringFromClass(Annotation.self)) if (annotationView == nil) { annotationView = AnnotationView(annotation: annotation, reuseIdentifier: NSStringFromClass(Annotation.self)) } else { annotationView!.annotation = annotation } if let annotation = annotation as? Annotation { annotation.headingDelegate = annotationView as? HeadingDelegate annotationView!.image = /* arrow image */ } return annotationView }
最重要的部分是在批注視圖對象中分配了批注的委托屬性( headingDelegate
)。 這會將注釋與其視圖綁定在一起,以便每次修改heading屬性時,都會調用該視圖的headingChanged()
方法。
注意:這里使用的 didSet{}
和willSet{}
屬性觀察器是在Swift 4中首次引入的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.