How to Drag and drop the annotation pin in Mkmapview
To make an annotation draggable, set the annotation view's draggable property to YES.
This is normally done in the viewForAnnotation delegate method.
For example:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *reuseId = @"pin";
MKPinAnnotationView *pav = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (pav == nil)
{
pav = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
pav.draggable = YES;
pav.canShowCallout = YES;
}
else
{
pav.annotation = annotation;
}
return pav;
}
If you need to handle when the user stops dragging and drops the annotation,
see: how to manage drag and drop for MKAnnotationView on IOS?
how to manage drag and drop for MKAnnotationView on IOS?
In addition, your annotation object (the one that implements MKAnnotation) should have a settable coordinate property. You are using the MKPointAnnotation class which does implement setCoordinate so that part's already taken care of.
iOS Swift MapKit making an annotation draggable by the user?
It isn't enough to mark the annotation view by setting isDraggable
to true
. You must also implement mapView(_:annotationView:didChange:fromOldState:)
in your map view delegate - and (even more important) this implementation must not be empty! Rather, your implementation must, at a minimum, communicate the drag state from the incoming parameters to the annotation view, like this:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
switch newState {
case .starting:
view.dragState = .dragging
case .ending, .canceling:
view.dragState = .none
default: break
}
}
Once you do that, the annotation will be properly draggable by the user.
(Many thanks to this answer for explaining this so clearly. I can't claim any credit! My answer here is merely a translation of that code into Swift.)
iOS Mapkit Annotation is not getting draggable
In your case annotation
is not an MKPointAnnotation
type. Thats why its not entering into if annotation is MKPointAnnotation {
condition.
It should be like:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is pinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinTintColor = .purple
pinAnnotationView.isDraggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
return pinAnnotationView
}
return nil
}
Because annotation
is pinAnnotation
type.
And I can not see mapView.delegate = self
in your code. But if you assign it via storyboard thats fine otherwise add it in viewDidLoad
method.
And your complete code will be:
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate{
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
let centerLocation = CLLocationCoordinate2D(latitude: 12.964370125970357, longitude: 77.59643554937497)
let mapspan = MKCoordinateSpanMake(0.01, 0.01)
let mapregion = MKCoordinateRegionMake(centerLocation, mapspan)
self.mapView.setRegion(mapregion, animated: true)
let pin = pinAnnotation(title:"hello",subtitle:"how are you",coordinate:centerLocation)
mapView.addAnnotation(pin)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is pinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")
pinAnnotationView.pinTintColor = .purple
pinAnnotationView.isDraggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true
return pinAnnotationView
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
switch newState {
case .starting:
view.dragState = .dragging
case .ending, .canceling:
view.dragState = .none
default: break
}
}
}
EDIT
You can get new location with view.annotation?.coordinate
check below code:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
switch newState {
case .starting:
view.dragState = .dragging
case .ending, .canceling:
//New cordinates
print(view.annotation?.coordinate)
view.dragState = .none
default: break
}
}
How to make annotation point draggable in MapKit using swift 2 and xcode 7?
pinAnnotationView.canShowCallout = false
This line solves my problem. Popup of showing "current location" was the main cause of this problem.
and these changes in code make it smooth dragging. Mehul and Vijay Answer help me in finding this solution.
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
switch (newState) {
case .Ending, .Canceling:
view.dragState = .None
default: break
}
}
MKMapKit draggable annotation and drawing polygons
To make the pins draggable, you need to set draggable = true
on the MKAnnotationView
. Implement the viewForAnnotation
and dequeue or create the annotation, then set draggable = true
. Ensure that the MKMapView
delegate is set otherwise none of the delegate methods will be called.
You may also find it easier to store the annotations in an array, rather than just storing the coordinates. The map view retains a reference to the annotations in the array, so when the point is moved in the map, the annotation is automatically updated.
Your question did not say whether you need to draw a path around the points, or through the points. If you want to draw an overlay which surrounds the points, then you also need to calculate the convex hull for the coordinates. The code example does this, although it's easily removed.
Example:
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()
}
}
Here is the convex hull sorting algorithm (adapted from this Gist on GitHub).
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)
}
This is how it would look with the convex hull sorting (path drawn around points):
This is how it looks without sorting (path drawn from point to point in sequence):
Is it possible to have draggable annotations in Swift when using MapKit
The settable coordinate
property belongs in the object that implements the MKAnnotation
protocol.
The object that implements the MKAnnotation
protocol is the one you pass to addAnnotation
.
The MKAnnotationView
class (and your CustomAnnotationView
subclass) are NOT the objects that implement MKAnnotation
. They are the visual representation of the annotation model objects.
Here's what the Location and Maps Programming Guide says:
To implement minimal support for dragging:
- In your annotation objects, implement the setCoordinate: method to
allow the map view to update the annotation’s coordinate point.- When creating your annotation view, set its draggable property to YES.
Your "annotation objects" are different things from your "annotation views".
In didTapGoButton
, the code is adding an annotation of type MKPlacemark
which does not implement setCoordinate:
.
Instead, you'll need to use the built-in MKPointAnnotation
class which does implement setCoordinate:
or create your own custom class that implements the MKAnnotation
protocol (not a subclass of MKAnnotationView
) and provides a settable coordinate
property.
So instead of this:
self.mapView.addAnnotation(MKPlacemark(placemark: placemark))
do this:
let pa = MKPointAnnotation()
pa.coordinate = placemark.location.coordinate
pa.title = placemark.name //or whatever title you like
self.mapView.addAnnotation(pa)
You can still keep your CustomAnnotationView
class if you like but it's not necessary just to create an annotation view with a custom image and it definitely does not need a coordinate property.
How Can I Drag My Annotation On MKmap View Like Google Pin Drop And Draging:iOS
First allow pin to drag
- (MKAnnotationView *) mapView: (MKMapView *) mapView viewForAnnotation: (id) annotation
{
pin.draggable = YES;
}
- (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)annotationView
didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
{
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = annotationView.annotation.coordinate;
[annotationView.annotation setCoordinate:droppedAt];
if(DEBUG_MODE){
NSLog(@"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
}
}
}
Related Topics
Add Uitapgesturerecognizer to Uitextview Without Blocking Textview Touches
How to Hide the Home Indicator with Swiftui
How to Draw on an Image in Swift
Phonegap on iOS with Absolute Path Urls for Assets
Stroke Width with a Scenekit Line Primitive Type
Facebooksdk(4.1.X) Custom Login UI Button - Swift(1.2)
Nspredicate for Array of Dictionaries
Why Must a Protocol Operator Be Implemented as a Global Function
Application Identifier Entitlement Value Has Changed
Set<Nsobject>' Does Not Have a Member Named 'Anyobject." - Xcode 6.3
Uibutton with Single Press and Long Press Events Swift
How to Convert Video (In Gallery) to Nsdata? in Swift
iOS Apns "Best-Effort" Fallback
iOS Are Methods Called by Delegates and Observers Executed on the Main Thread