为什么我的 MKPointAnnotation 不可拖动,我该如何更改它?

Why my MKPointAnnotation not draggable and how can I change it?

我有以下代码从用户那里获取 gps 坐标并在他当前所在的位置放置一个红色标记:

class ViewController: UIViewController, CLLocationManagerDelegate {

    @IBOutlet var mapView: MKMapView!
    var locationManager: CLLocationManager?

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager = CLLocationManager()
        locationManager!.delegate = self

        if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
            locationManager!.startUpdatingLocation()
        } else {
            locationManager!.requestWhenInUseAuthorization()
        }
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {


        switch status {
        case .NotDetermined:
            print("NotDetermined")
        case .Restricted:
            print("Restricted")
        case .Denied:
            print("Denied")
        case .AuthorizedAlways:
            print("AuthorizedAlways")
        case .AuthorizedWhenInUse:
            print("AuthorizedWhenInUse")
            locationManager!.startUpdatingLocation()
        }
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        let location = locations.first!
        let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 500, 500)
        mapView.setRegion(coordinateRegion, animated: true)
        locationManager?.stopUpdatingLocation()

        let annotation = MKPointAnnotation()
        annotation.coordinate = location.coordinate


        longitude = location.coordinate.longitude
        latitude = location.coordinate.latitude


        mapView.addAnnotation(annotation)


        locationManager = nil
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("Failed to initialize GPS: ", error.description)
    }
}

效果很好,如您所见,我正在使用这些行来获取用户的纬度和经度以便稍后进行处理:

        longitude = location.coordinate.longitude
        latitude = location.coordinate.latitude

现在我想添加另一个功能 - 用户在地图上看到他的当前位置作为红色大头针,可以保持原样,或者 - 新功能 - 他可以将红色小人拖到其他地方并获得它的新位置经度和纬度。

我一直在到处查看可拖动功能,我决定将这段代码添加到我现有的代码中:

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    if annotation is MKPointAnnotation {
        let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")

        pinAnnotationView.pinColor = .Purple
        pinAnnotationView.draggable = true
        pinAnnotationView.canShowCallout = true
        pinAnnotationView.animatesDrop = true

        return pinAnnotationView
    }

    return nil
}

但它并没有成功,图钉仍然不可拖动。我该如何解决这个问题并在每次用户抓住它并四处移动时获取(目前 - 打印到控制台)pin 的新位置?

如果您看到的是红色标记而不是您在代码示例中指定的紫色标记,则表明您尚未注册注释视图的重用标识符 and/or mapView(_:viewFor:)没有被调用。

现在,为了帮助避免视图控制器膨胀,我们通常将注释视图的自定义放在它自己的子class中(本着“单一职责原则”的精神):

class CustomAnnotationView: MKPinAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

        pinTintColor = .purple
        isDraggable = true
        canShowCallout = true
        animatesDrop = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

然后,在 iOS 11 和更高版本中,您可能会完全删除 mapView(_:viewFor:) 而只是注册 class 就完成了:

override func viewDidLoad() {
    super.viewDidLoad()

    mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
}

在 11 之前的 iOS 版本中(或者如果您有一些复杂的逻辑,您可能需要添加多种类型的注释),您需要指定 delegate of the map view and implement mapView(_:viewFor:)

可以通过 control-从 IB 中“Connections Inspector”中的 delegate outlet 拖动,或者通过编程方式连接 IB 中的委托,例如在 viewDidLoad:

mapView.delegate = self

然后我会添加一个常量来指定我首选的自定义注释视图的重用标识符:

class CustomAnnotationView: MKPinAnnotationView {
    static let reuseIdentifier = Bundle.main.bundleIdentifier! + ".customAnnotationView"

    // these two are unchanged from as shown above

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) { ... }        
    required init?(coder aDecoder: NSCoder) { ... }
}

然后,您将实现 mapView(_:viewFor:),这将尝试使先前的注释视图出列并更新其 annotation,如果可能,或者实例化一个新的注释视图,如果不是:

extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation { return nil }

        if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: CustomAnnotationView.reuseIdentifier) {
            annotationView.annotation = annotation
            return annotationView
        }

        return CustomAnnotationView(annotation: annotation, reuseIdentifier: CustomAnnotationView.reuseIdentifier)
    }
}

请参阅 previous iteration of this answer 了解较早的 Swift 版本。

提示:选中的注释可以拖动,只有显示标注时才能选中。当它有标题时,它可以显示标注。所以除非你有一个标题,否则它不会起作用。