25

I want to add annotation on touch of iOS map and fetch the detailed address (Placemark) of respective location. How I can achieve this in Swift?

Thanks in advance.

SRK
  • 744
  • 3
  • 11
  • 23
  • He specifically mentions "Placemark." I found this post because I was searching for how to get a Placemark from an Annotation. I was encouraged that this thread would answer that, because that's what the OP is asking for. But, in any case, an answer that only address half the question cannot be considered complete. – Zonker.in.Geneva Aug 26 '19 at 09:37
  • 1
    use this -> https://github.com/almassapargali/LocationPicker – YodagamaHeshan Jun 04 '20 at 03:44

7 Answers7

28

To react to the touch on map you need to set up a tap recogniser for the mapView

in viewDidLoad:

let gestureRecognizer = UITapGestureRecognizer(
                              target: self, action:#selector(handleTap))
    gestureRecognizer.delegate = self
    mapView.addGestureRecognizer(gestureRecognizer)

Handle the tap and get the tapped location coordinates:

func handleTap(gestureRecognizer: UITapGestureRecognizer) {
    
    let location = gestureRecognizer.location(in: mapView)
    let coordinate = mapView.convert(location, toCoordinateFrom: mapView)
    
    // Add annotation:
    let annotation = MKPointAnnotation()
    annotation.coordinate = coordinate
    mapView.addAnnotation(annotation)
}

Now you only have to implement the MKMapView delegate functions to draw the annotations. A simple google search should get you the rest of that.

Fattie
  • 27,874
  • 70
  • 431
  • 719
Moriya
  • 7,750
  • 3
  • 35
  • 53
  • 1
    I've added an edit for swift 3 @Neo42. You'll be able to see it once it's peer reviewed – KyleHodgetts Aug 20 '17 at 13:09
  • This "answer" doesn't answer the user's question. The OP asked how to get a Placemark. – Zonker.in.Geneva Aug 26 '19 at 06:53
  • You should never use a string literal to identify a selector unless there is no other option. Instead, use `#selector(handleTap(gestureRecognizer:))`. This prevents typos and allows you to use Xcode's refactoring tools. – Peter Schorn Aug 04 '20 at 15:04
10

Here is a working Xcode 10.1, Swift 4.2 project with MapKit delegates to control the annotations (pin color, pin image etc.) and delegate to handle a click on the added annotation: Github Project

import UIKit
import MapKit

class ViewController: UIViewController {

@IBOutlet weak var mapView: MKMapView!

override func viewDidLoad() {
    super.viewDidLoad()
    mapView.delegate = self
    let longTapGesture = UILongPressGestureRecognizer(target: self, action: #selector(longTap))
    mapView.addGestureRecognizer(longTapGesture)
}

@objc func longTap(sender: UIGestureRecognizer){
    print("long tap")
    if sender.state == .began {
        let locationInView = sender.location(in: mapView)
        let locationOnMap = mapView.convert(locationInView, toCoordinateFrom: mapView)
        addAnnotation(location: locationOnMap)
    }
}

func addAnnotation(location: CLLocationCoordinate2D){
        let annotation = MKPointAnnotation()
        annotation.coordinate = location
        annotation.title = "Some Title"
        annotation.subtitle = "Some Subtitle"
        self.mapView.addAnnotation(annotation)
}
}

extension ViewController: MKMapViewDelegate{

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    guard annotation is MKPointAnnotation else { print("no mkpointannotaions"); return nil }

    let reuseId = "pin"
    var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView

    if pinView == nil {
        pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        pinView!.canShowCallout = true
        pinView!.rightCalloutAccessoryView = UIButton(type: .infoDark)
        pinView!.pinTintColor = UIColor.black
    }
    else {
        pinView!.annotation = annotation
    }
    return pinView
}

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    print("tapped on pin ")
}

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
    if control == view.rightCalloutAccessoryView {
        if let doSomething = view.annotation?.title! {
           print("do something")
        }
    }
  }
}
Peter Pohlmann
  • 1,478
  • 15
  • 30
  • This "answer" doesn't answer the user's question. The OP asked how to get a Placemark. – Zonker.in.Geneva Aug 26 '19 at 06:54
  • Please read the question carefully again and then think about it again. The Question is how to add an annotation on touch & get the address. My answer covers 50% of it. Just because it doesn't answer your question, doesn't mean it is false. – Peter Pohlmann Aug 26 '19 at 09:32
  • To quote the OP: "...fetch the detailed address (Placemark)" he's asking for a Placemark. – Zonker.in.Geneva Aug 26 '19 at 09:34
  • No, but it seems you are. Yes, he asked for two things: placing an Annotation and getting a Placemark, not just the detailed address. Not a single answer addresses the Placemark issue. Partial credit for partial answers. – Zonker.in.Geneva Aug 26 '19 at 09:40
  • I never said your answer was false, by the way. But a 50% answer is not an answer. If someone asked for directions from Berlin to Frankfurt and I said, "Go to the end of the street and turn left," I'd hardly be applauded for answering the original question. – Zonker.in.Geneva Aug 26 '19 at 09:45
  • Nothing you are 100% right. Hope you find your answers. Have a good day. – Peter Pohlmann Aug 26 '19 at 09:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/198474/discussion-between-zonker-in-geneva-and-peter-pohlmann). – Zonker.in.Geneva Aug 26 '19 at 10:13
2

Swift 4:

 @IBOutlet weak var mapView: MKMapView!

 func handleLongPress (gestureRecognizer: UILongPressGestureRecognizer) {
    if gestureRecognizer.state == UIGestureRecognizerState.began {
        let touchPoint: CGPoint = gestureRecognizer.location(in: mapView)
        let newCoordinate: CLLocationCoordinate2D = mapView.convert(touchPoint, toCoordinateFrom: mapView)
        addAnnotationOnLocation(pointedCoordinate: newCoordinate)
    }
}

func addAnnotationOnLocation(pointedCoordinate: CLLocationCoordinate2D {
    let annotation = MKPointAnnotation()
    annotation.coordinate = pointedCoordinate
    annotation.title = "Loading..."
    annotation.subtitle = "Loading..."
    mapView.addAnnotation(annotation)
}
hacker_1989
  • 303
  • 4
  • 15
Sat
  • 29
  • 2
1

For Swift 4 I converted the Swift 3 example as the other Swift 4 one did not work for me: (note I'm using 'mapview' instead of 'mapView'just to fit in with other code

        let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
        gestureRecognizer.delegate = self
        mapview.addGestureRecognizer(gestureRecognizer)


@objc func handleTap(_ gestureReconizer: UILongPressGestureRecognizer)
    {

        let location = gestureReconizer.location(in: mapview)
        let coordinate = mapview.convert(location,toCoordinateFrom: mapview)

        // Add annotation:
        let annotation = MKPointAnnotation()
        annotation.coordinate = coordinate
        mapview.addAnnotation(annotation)
    }
SundialSoft
  • 136
  • 8
1

Since the other answers adequately cover how to handle the touch event, the next step is to use CLGeocoder to preform a reverse-geocoding, which will convert a location value into a list of placemarks at that location.

func handleTap(gestureReconizer: UILongPressGestureRecognizer) {

    let location = gestureReconizer.locationInView(mapView)
    let coordinate = mapView.convertPoint(location,toCoordinateFromView: mapView)

    let geocoder = CLGeocoder()
    geocoder.reverseGeocodeLocation(coordinate) { (placemarks, error) in 
        if let places = placemarks {
            for place in places {
                print("found placemark \(place.name) at address \(place.postalAddress)"
            }
        }
    }
}
Devin Lane
  • 964
  • 1
  • 7
  • 14
0

For swift 3.0

let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
gestureRecognizer.delegate = self
mapView.addGestureRecognizer(gestureRecognizer)

func handleTap(_ gestureReconizer: UILongPressGestureRecognizer) {

    let location = gestureReconizer.locationInView(mapView)
    let coordinate = mapView.convertPoint(location,toCoordinateFromView: mapView)

    // Add annotation:
    let annotation = MKPointAnnotation()
    annotation.coordinate = coordinate
    mapView.addAnnotation(annotation)
}
0
 //put this in viewdidload

-->
 mapView.delegate = self
           let longTapGesture = UILongPressGestureRecognizer(target: self, action: #selector(longTap))
           mapView.addGestureRecognizer(longTapGesture)
    -->//

@objc func longTap(sender: UIGestureRecognizer){
    print("long tap")
    if sender.state == .began
    {
        let locationInView = sender.location(in: mapView)
        let locationOnMap = mapView.convert(locationInView, toCoordinateFrom: mapView)
        addAnnotation(location: locationOnMap)
        locationManager.stopUpdatingLocation();
        print("the location lattitude is = \(locationOnMap.latitude) and logitude on map = \(locationOnMap.longitude)")
        self.passlat = Double(locationOnMap.latitude)
        self.passlong = Double(locationOnMap.longitude)
        self.getAddressFromLatLon(pdblLatitude: "\(locationOnMap.latitude)", withLongitude: "\(locationOnMap.longitude)")

    }
}


func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String)
{
    var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
    let lat: Double = Double("\(pdblLatitude)")!
    //21.228124
    let lon: Double = Double("\(pdblLongitude)")!
    //72.833770
    let ceo: CLGeocoder = CLGeocoder()
    center.latitude = lat
    center.longitude = lon

    let loc: CLLocation = CLLocation(latitude:center.latitude, longitude: center.longitude)


    ceo.reverseGeocodeLocation(loc, completionHandler:
        {(placemarks, error) in
            if (error != nil)
            {
                print("reverse geodcode fail: \(error!.localizedDescription)")
            }
            let pm = placemarks! as [CLPlacemark]
            if pm.count > 0
            {
                let pm = placemarks![0]
               // print(pm.country)
                //print(pm.locality)
                self.mapaddcontry = pm.country!
                self.mapaddrState = pm.subLocality!
                self.mapaddrcity = pm.locality!
                self.mapaddrPincode = pm.postalCode!

                self.mainname = pm.locality!
                print(pm.subLocality)
                self.subname = pm.subLocality!
                print(pm.thoroughfare)
                print(pm.postalCode)
                print(pm.subThoroughfare)
                var addressString : String = ""
                if pm.subLocality != nil
                {
                    addressString = addressString + pm.subLocality! + ", "
                }
                if pm.thoroughfare != nil {
                    addressString = addressString + pm.thoroughfare! + ", "
                }
                if pm.locality != nil {
                    addressString = addressString + pm.locality! + ", "
                }
                if pm.country != nil
                {
                    addressString = addressString + pm.country! + ", "
                }
                if pm.postalCode != nil
                {
                    addressString = addressString + pm.postalCode! + " "
                }

                self.addr.text = addressString
                print(addressString)
                self.mapaddrtxt.text = addressString
                self.location_name = addressString

            }
    })

}
Anurag Sharma
  • 4,276
  • 2
  • 28
  • 44
kiran
  • 76
  • 5
  • @anurag mkmapView.addAnnotation(locationOnMap as! MKAnnotation) it's getting an error Could not cast value of type '__C.CLLocationCoordinate2D' (0x10c801f88) to '__C.MKAnnotation' (0x7fd45a94e6c8). – Sreekanth G Sep 02 '20 at 18:53