I am trying to use a number of UIGestureReconizers
with an MKMapView
onto which the user can drop a pin and drag it around. It has a callout
. I am implementing this in a TabbarController
and also within a NavigationController
. I currently have:
1) A PanGestureRecognizer
animates the Tabbar and Navigation item off the screen. This works fine without interfering with panning the map.
2) A TapGestureRecognizer
set to one tap animates the two items from 1) back onto the screen.
3) A TapGestureRecognizer
set to two taps allows the underlying MKMapView
zoom functionality to work. This GestureRecognizer's
delegate
has gestureRecognizer.shouldRecognizeSimultaneouslyWithGestureRecognizer
set to true
These are setup in viewDidLoad
as follows:
// This sets up the pan gesture recognizer to hide the bars from the UI.
let panRec: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: "didDragMap:")
panRec.delegate = self
mapView.addGestureRecognizer(panRec)
// This sets up the tap gesture recognizer to un-hide the bars from the UI.
let singleTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "didTapMap:")
singleTap.delegate = self
singleTap.numberOfTapsRequired = 1
singleTap.numberOfTouchesRequired = 1
mapView.addGestureRecognizer(singleTap)
// This sets up the double tap gesture recognizer to enable the zoom facility.
// In order to pass double-taps to the underlying `MKMapView` the delegate for this recognizer (self) needs to return true from
// gestureRecognizer.shouldRecognizeSimultaneouslyWithGestureRecognizer
let doubleTap: UITapGestureRecognizer = UITapGestureRecognizer()
doubleTap.numberOfTapsRequired = 2
doubleTap.numberOfTouchesRequired = 1
doubleTap.delegate = self
mapView.addGestureRecognizer(doubleTap)
// This delays the single-tap recognizer slightly and ensures that it will NOT fire if there is a double-tap
singleTap.requireGestureRecognizerToFail(doubleTap)
My problem occurs when I try to implement a UILongPressGestureRecognizer
to allow the dropping of a pin onto the map. I'm trying to use the following added to viewDidLoad
:
// This sets up the long tap to drop the pin.
let longTap: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "didLongTapMap:")
longTap.delegate = self
longTap.numberOfTapsRequired = 0
longTap.minimumPressDuration = 0.5
mapView.addGestureRecognizer(longTap)
This is my action method:
func didLongTapMap(gestureRecognizer: UIGestureRecognizer) {
// Get the spot that was tapped.
let tapPoint: CGPoint = gestureRecognizer.locationInView(mapView)
let touchMapCoordinate: CLLocationCoordinate2D = mapView.convertPoint(tapPoint, toCoordinateFromView: mapView)
var viewAtBottomOfHierarchy: UIView = mapView.hitTest(tapPoint, withEvent: nil)
if let viewAtBottom = viewAtBottomOfHierarchy as? MKPinAnnotationView {
return
} else {
if .Began == gestureRecognizer.state {
// Delete any existing annotations.
if mapView.annotations.count != 0 {
mapView.removeAnnotations(mapView.annotations)
}
annotation = MKPointAnnotation()
annotation.coordinate = touchMapCoordinate
mapView.addAnnotation(annotation)
_isPinOnMap = true
findAddressFromCoordinate(annotation.coordinate)
updateLabels()
}
}
}
This does indeed allow a pin to be dropped on a long tap and a single tap will display the callout BUT a second tap to hold and drag causes a second pin to drop if the drag isn't started sufficiently quickly. This second pin drops into the space the previous pin was hovering in and can be dragged by the user, but the new pin dropping is awkward and wrong looking.
I'm trying to use the line:
if let viewAtBottom = viewAtBottomOfHierarchy as? MKPinAnnotationView {
to return the tap to the MKMapView
and prevent another pin being dropped but the return never gets called even though a breakpoint on this line shows viewAtBottom is of type MapKit.MKPinAnnotationView
. Any ideas where I'm going wrong?
I think I might have the answer to your problem if I understood it correctly. Your having problems when one pin is dropped and then dragging the screen around without placing another pin, correct? This is my code, I have been making something similar and this seems to work for me. import UIKit import MapKit import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
@IBOutlet var map: MKMapView!
var manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let uilpgr = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.longpress(gestureRecognizer:)))
uilpgr.minimumPressDuration = 2
map.addGestureRecognizer(uilpgr)
if activePlace == -1 {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
self.map.showsUserLocation = true
} else {
//GET PLACE DETAILS TO DISPLAY ON MAP
if places.count > activePlace {
if let name = places[activePlace]["name"]{
if let lat = places[activePlace]["lat"]{
if let lon = places[activePlace]["lon"]{
if let latitude = Double(lat) {
if let longitude = Double(lon) {
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region = MKCoordinateRegionMake(coordinate, span)
self.map.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = name
self.map.addAnnotation(annotation)
}
}
}
}
}
}
}
}
func longpress(gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.began {
let touchPoint = gestureRecognizer.location(in: self.map)
let newCoordinate = self.map.convert(touchPoint, toCoordinateFrom: self.map)
let location = CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude)
var title = ""
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) in
if error != nil {
print(error)
} else {
if let placemark = placemarks?[0] {
if placemark.subThoroughfare != nil {
title += placemark.subThoroughfare! + " "
}
if placemark.thoroughfare != nil {
title += placemark.thoroughfare! + " "
}
}
}
if title == "" {
title = "Added \(NSDate())"
}
let annotation = MKPointAnnotation()
annotation.coordinate = newCoordinate
annotation.title = title
self.map.addAnnotation(annotation)
places.append(["name": title, "lat":String(newCoordinate.latitude), "lon":String(newCoordinate.longitude)])
UserDefaults.standard.set(places, forKey: "places")
})
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = CLLocationCoordinate2D(latitude: locations[0].coordinate.latitude, longitude: locations[0].coordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let region = MKCoordinateRegion(center: location, span: span)
self.map.setRegion(region, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Hope it helps, the problem might also be that your minimum longpress duration is only 0.5.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With