我认为解析代码直接操作注释视图是不合适的。您不知道它们是否可见,是否已实例化等等。地图视图负责管理注释视图,而不是您。
如果您需要维护总线和注释之间的交叉引用,请执行此操作,但不要维护对注释视图的引用。您的应用程序与注释的交互应仅限于注释本身。因此创建一个注释子类,它具有angle
财产。
class MyAnnotation : MKPointAnnotation {
@objc dynamic var angle: CGFloat = 0.0
}
然后,您可以让注释视图子类“观察”自定义注释子类,并随着注释的旋转而旋转angle
变化。例如,在 Swift 4 中:
class MyAnnotationView : MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
addAngleObserver()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addAngleObserver()
}
// Remember, since annotation views can be reused, if the annotation changes,
// remove the old annotation's observer, if any, and add new one's.
override var annotation: MKAnnotation? {
willSet { token = nil }
didSet { addAngleObserver() }
}
// add observer
var token: NSKeyValueObservation!
private func addAngleObserver() {
if let annotation = annotation as? MyAnnotation {
transform = CGAffineTransform(rotationAngle: annotation.angle)
token = annotation.observe(\.angle) { [weak self] annotation, _ in
UIView.animate(withDuration: 0.25) {
self?.transform = CGAffineTransform(rotationAngle: annotation.angle)
}
}
}
}
}
或者在 Swift 3 中:
private var angleObserverContext = 0
class MyAnnotationView : MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
addAngleObserver()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addAngleObserver()
}
// add observer
private func addAngleObserver() {
if let annotation = annotation as? MyAnnotation {
transform = CGAffineTransform(rotationAngle: annotation.angle)
annotation.addObserver(self, forKeyPath: #keyPath(MyAnnotation.angle), options: [.new, .old], context: &angleObserverContext)
}
}
// remove observer
private func removeAngleObserver() {
if let annotation = annotation as? MyAnnotation {
annotation.removeObserver(self, forKeyPath: #keyPath(MyAnnotation.angle))
}
}
// remember to remove observer when annotation view is deallocated
deinit {
removeAngleObserver()
}
// Remember, since annotation views can be reused, if the annotation changes,
// remove the old annotation's observer, if any, and add new one's.
override var annotation: MKAnnotation? {
willSet { removeAngleObserver() }
didSet { addAngleObserver() }
}
// Handle observation events for the annotation's `angle`, rotating as appropriate
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &angleObserverContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
UIView.animate(withDuration: 0.5) {
if let angleNew = change![.newKey] as? CGFloat {
self.transform = CGAffineTransform(rotationAngle: angleNew)
}
}
}
}
现在,您的应用程序可以维护对已添加到地图中的注释的引用,并设置它们angle
这将适当地在地图视图中直观地表示。
并且,使用这个的一个快速而肮脏的例子:
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
var annotation = MyAnnotation()
private let reuseIdentifer = Bundle.main.bundleIdentifier! + ".annotation"
private lazy var manager: CLLocationManager = {
let manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
return manager
}()
override func viewDidLoad() {
super.viewDidLoad()
mapView.register(MyAnnotationView.self, forAnnotationViewWithReuseIdentifier: reuseIdentifer)
manager.requestWhenInUseAuthorization()
manager.startUpdatingHeading()
manager.startUpdatingLocation()
mapView.addAnnotation(annotation)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
return mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifer, for: annotation)
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last,
location.horizontalAccuracy >= 0 else {
return
}
annotation.coordinate = location.coordinate
}
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
guard newHeading.headingAccuracy >= 0 else { return }
annotation.angle = CGFloat(newHeading.trueHeading * .pi / 180)
}
}
See 此答案的先前修订版 https://stackoverflow.com/revisions/33464788/2以 Swift 2 为例。