2016-07-13 8 views
2

Ich versuche derzeit, dem Benutzer das Hinzufügen von Pins zur Karte zu ermöglichen, die dann ein Polygon zeichnen, das diese Pins verbindet. Ich möchte es jedoch erweitern, damit der Benutzer die Pins ziehen kann und die Polygone entsprechend aktualisiert werden. MKMapView zeichnet das Polygon aus dem Array von Koordinaten entsprechend ihrer Anordnung im Array (wenn ich mich nicht irre). Das Problem, mit dem ich jetzt konfrontiert bin, ist, wie ich die Polygone aktualisiere, nachdem der Benutzer die Pins neu positioniert hat.MKMapKit ziehbare Annotations- und Zeichenpolygone

var touchCoordinatesWithOrder: [(coordinate: CLLocationCoordinate2D, order: Int)] = [] 
var counter = 0 

func addLongPressGesture() { 
    let longPressRecogniser = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress)) 
    longPressRecogniser.minimumPressDuration = 1.0 
    mapView.addGestureRecognizer(longPressRecogniser) 

} 

func handleLongPress(gestureRecognizer: UIGestureRecognizer) { 
    if gestureRecognizer.state != .Began { 
     return 
    } 

    let touchPoint = gestureRecognizer.locationInView(self.mapView) 
    let touchMapCoordinate = mapView.convertPoint(touchPoint, toCoordinateFromView: mapView) 

    let annotation = MKPointAnnotation() 
    annotation.coordinate = touchMapCoordinate 
    mapView.addAnnotation(annotation) 

    touchCoordinatesWithOrder.append((coordinate: touchMapCoordinate, order: counter)) 
    counter += 1 

} 


@IBAction func drawAction(sender: AnyObject) { 
    if touchCoordinatesWithOrder.count <= 2 { 
     print("Not enough coordinates") 
     return 
    } 

    var coords = [CLLocationCoordinate2D]() 
    for i in 0..<touchCoordinatesWithOrder.count { 
     coords.append(touchCoordinatesWithOrder[i].coordinate) 
    } 

    let polygon = MKPolygon(coordinates: &coords, count: coords.count) 
    mapView.addOverlay(polygon) 
    counter = 0 
} 

func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) { 
    // if the user repositioned pin number2 then how to I update my array? 
} 

func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer { 
    if overlay is MKPolygon { 
     let polygonView = MKPolygonRenderer(overlay: overlay) 
     polygonView.strokeColor = UIColor.blackColor() 
     polygonView.lineWidth = 0.5 
     return polygonView 
    } 
    return MKPolylineRenderer() 
} 

Antwort

4

Um die Stifte ziehbar zu machen, müssen Sie draggable = true auf dem MKAnnotationView einzustellen. Implementieren Sie die viewForAnnotation und entfernen Sie die Verknüpfung oder erstellen Sie die Anmerkung, und legen Sie dann draggable = true fest. Stellen Sie sicher, dass der Delegat MKMapView festgelegt ist, andernfalls wird keine der Delegate-Methoden aufgerufen.

Sie können es auch einfacher finden, die Anmerkungen in einem Array zu speichern, anstatt nur die Koordinaten zu speichern. Die Kartenansicht enthält einen Verweis auf die Anmerkungen im Array. Wenn der Punkt in der Karte verschoben wird, wird die Anmerkung automatisch aktualisiert.

Ihre Frage nicht sagen, ob Sie einen Pfad um die Punkte zeichnen müssen, oder durch die Punkte. Wenn Sie ein Overlay zeichnen möchten, das die Punkte umgibt, müssen Sie auch die konvexe Hülle für die Koordinaten berechnen. Das Codebeispiel macht das, obwohl es leicht entfernt werden kann.

Beispiel:

class MapAnnotationsOverlayViewController: UIViewController, MKMapViewDelegate { 

    @IBOutlet var mapView: MKMapView! 

    // Array of annotations - modified when the points are changed. 
    var annotations = [MKPointAnnotation]() 

    // Current polygon displayed in the overlay. 
    var polygon: MKPolygon? 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     mapView.delegate = self  
     addLongPressGesture() 
    } 

    func addLongPressGesture() { 
     let longPressRecogniser = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress)) 
     longPressRecogniser.minimumPressDuration = 0.25 
     mapView.addGestureRecognizer(longPressRecogniser) 

    } 

    func handleLongPress(gestureRecognizer: UIGestureRecognizer) { 

     guard gestureRecognizer.state == .Began else { 
      return 
     } 

     let touchPoint = gestureRecognizer.locationInView(self.mapView) 
     let touchMapCoordinate = mapView.convertPoint(touchPoint, toCoordinateFromView: mapView) 


     let annotation = MKPointAnnotation() 

     // The annotation must have a title in order for it to be selectable. 
     // Without a title the annotation is not selectable, and therefore not draggable. 
     annotation.title = "Point \(annotations.count)" 
     annotation.coordinate = touchMapCoordinate 
     mapView.addAnnotation(annotation) 

     // Add the new annotation to the list. 
     annotations.append(annotation) 

     // Redraw the overlay. 
     updateOverlay() 
    } 

    @IBAction func drawAction(sender: AnyObject) { 
     updateOverlay() 
    } 

    func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { 

     var view = mapView.dequeueReusableAnnotationViewWithIdentifier("pin") 

     if let view = view { 
      view.annotation = annotation 
     } 
     else { 
      view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin") 

      // Allow the pin to be repositioned. 
      view?.draggable = true 
     } 

     return view 
    } 

    func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) { 

    // The map view retains a reference to the same annotations in the array. 
    // The annotation in the array is automatically updated when the pin is moved. 

     updateOverlay() 
    } 

    func updateOverlay() { 

     // Remove existing overlay. 
     if let polygon = self.polygon { 
      mapView.removeOverlay(polygon) 
     } 

     self.polygon = nil 

     if annotations.count < 3 { 
      print("Not enough coordinates") 
      return 
     } 

     // Create coordinates for new overlay. 
     let coordinates = annotations.map({ $0.coordinate }) 

     // Sort the coordinates to create a path surrounding the points. 
     // Remove this if you only want to draw lines between the points. 
     var hull = sortConvex(coordinates) 

     let polygon = MKPolygon(coordinates: &hull, count: hull.count) 
     mapView.addOverlay(polygon) 

     self.polygon = polygon 
    } 

    func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer { 
     if overlay is MKPolygon { 
      let polygonView = MKPolygonRenderer(overlay: overlay) 
      polygonView.strokeColor = UIColor.blackColor() 
      polygonView.lineWidth = 0.5 
      return polygonView 
     } 
     return MKPolylineRenderer() 
    } 
} 

Hier ist die konvexe Hülle Sortieralgorithmus (von diesem Gist on GitHub angepasst).

func sortConvex(input: [CLLocationCoordinate2D]) -> [CLLocationCoordinate2D] { 

    // X = longitude 
    // Y = latitude 

    // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product. 
    // Returns a positive value, if OAB makes a counter-clockwise turn, 
    // negative for clockwise turn, and zero if the points are collinear. 
    func cross(P: CLLocationCoordinate2D, _ A: CLLocationCoordinate2D, _ B: CLLocationCoordinate2D) -> Double { 
     let part1 = (A.longitude - P.longitude) * (B.latitude - P.latitude) 
     let part2 = (A.latitude - P.latitude) * (B.longitude - P.longitude) 
     return part1 - part2; 
    } 

    // Sort points lexicographically 
    let points = input.sort() { 
     $0.longitude == $1.longitude ? $0.latitude < $1.latitude : $0.longitude < $1.longitude 
    } 

    // Build the lower hull 
    var lower: [CLLocationCoordinate2D] = [] 
    for p in points { 
     while lower.count >= 2 && cross(lower[lower.count-2], lower[lower.count-1], p) <= 0 { 
      lower.removeLast() 
     } 
     lower.append(p) 
    } 

    // Build upper hull 
    var upper: [CLLocationCoordinate2D] = [] 
    for p in points.reverse() { 
     while upper.count >= 2 && cross(upper[upper.count-2], upper[upper.count-1], p) <= 0 { 
      upper.removeLast() 
     } 
     upper.append(p) 
    } 

    // Last point of upper list is omitted because it is repeated at the 
    // beginning of the lower list. 
    upper.removeLast() 

    // Concatenation of the lower and upper hulls gives the convex hull. 
    return (upper + lower) 
} 

Dies ist, wie es mit der konvexen Hülle Sortierung (Pfad gezogen um Punkte) aussehen:

Convex hull surrounding points

Dies ist, wie es aussieht, ohne Sortierung (Pfad von Punktfolge weisen gezogen):

Point-to-point path

+0

Danke. Es hat gut funktioniert. – John