Draggable annotation views
This example uses two classes within a single file:
DraggableAnnotationView
is a subclass ofMGLAnnotationView
, a descendant ofUIView
.ViewController
uses these annotation views withMGLMapView
.
Similar to the Annotation views example, this example demonstrates how native UIView
s can be used to mark locations on a map. For more information about working with gesture recognizers, see our User interactions guide.
To learn about more ways to add points to a map, see the Markers and annotations guide.
ViewController
import Mapbox // Example view controllerclass ViewController: UIViewController, MGLMapViewDelegate {override func viewDidLoad() {super.viewDidLoad() let mapView = MGLMapView(frame: view.bounds)mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]mapView.styleURL = MGLStyle.lightStyleURLmapView.tintColor = .darkGraymapView.zoomLevel = 1mapView.delegate = selfview.addSubview(mapView) // Specify coordinates for our annotations.let coordinates = [CLLocationCoordinate2D(latitude: 0, longitude: -70),CLLocationCoordinate2D(latitude: 0, longitude: -35),CLLocationCoordinate2D(latitude: 0, longitude: 0),CLLocationCoordinate2D(latitude: 0, longitude: 35),CLLocationCoordinate2D(latitude: 0, longitude: 70)] // Fill an array with point annotations and add it to the map.var pointAnnotations = [MGLPointAnnotation]()for coordinate in coordinates {let point = MGLPointAnnotation()point.coordinate = coordinatepoint.title = "To drag this annotation, first tap and hold."pointAnnotations.append(point)} mapView.addAnnotations(pointAnnotations)} // MARK: - MGLMapViewDelegate methods // This delegate method is where you tell the map to load a view for a specific annotation. To load a static MGLAnnotationImage, you would use `-mapView:imageForAnnotation:`.func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {// This example is only concerned with point annotations.guard annotation is MGLPointAnnotation else {return nil} // For better performance, always try to reuse existing annotations. To use multiple different annotation views, change the reuse identifier for each.if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "draggablePoint") {return annotationView} else {return DraggableAnnotationView(reuseIdentifier: "draggablePoint", size: 50)}} func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {return true}} // MGLAnnotationView subclassclass DraggableAnnotationView: MGLAnnotationView {init(reuseIdentifier: String, size: CGFloat) {super.init(reuseIdentifier: reuseIdentifier) // `isDraggable` is a property of MGLAnnotationView, disabled by default.isDraggable = true // This property prevents the annotation from changing size when the map is tilted.scalesWithViewingDistance = false // Begin setting up the view.frame = CGRect(x: 0, y: 0, width: size, height: size) backgroundColor = .darkGray // Use CALayer’s corner radius to turn this view into a circle.layer.cornerRadius = size / 2layer.borderWidth = 1layer.borderColor = UIColor.white.cgColorlayer.shadowColor = UIColor.black.cgColorlayer.shadowOpacity = 0.1} // These two initializers are forced upon us by Swift.override init(frame: CGRect) {super.init(frame: frame)} required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")} // Custom handler for changes in the annotation’s drag state.override func setDragState(_ dragState: MGLAnnotationViewDragState, animated: Bool) {super.setDragState(dragState, animated: animated) switch dragState {case .starting:print("Starting", terminator: "")startDragging()case .dragging:print(".", terminator: "")case .ending, .canceling:print("Ending")endDragging()case .none:break@unknown default:fatalError("Unknown drag state")}} // When the user interacts with an annotation, animate opacity and scale changes.func startDragging() {UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: {self.layer.opacity = 0.8self.transform = CGAffineTransform.identity.scaledBy(x: 1.5, y: 1.5)}, completion: nil) // Initialize haptic feedback generator and give the user a light thud.if #available(iOS 10.0, *) {let hapticFeedback = UIImpactFeedbackGenerator(style: .light)hapticFeedback.impactOccurred()}} func endDragging() {transform = CGAffineTransform.identity.scaledBy(x: 1.5, y: 1.5)UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: {self.layer.opacity = 1self.transform = CGAffineTransform.identity.scaledBy(x: 1, y: 1)}, completion: nil) // Give the user more haptic feedback when they drop the annotation.if #available(iOS 10.0, *) {let hapticFeedback = UIImpactFeedbackGenerator(style: .light)hapticFeedback.impactOccurred()}}}