LongPress in MapKit - swift

I am bulding an app in Swift 3.
Everything seams to be working ok however when I longPress in the map it acts like if it was longpressed twice.
I don't know why...
i've made a print inside the longpress, to count the longpresses and every time I longpress (one time) it detects two long presses... why is this happening? Whats wrong with it?
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet var map: MKMapView!
var numberOfLongPress : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
let latitude: CLLocationDegrees = 38.925560
let longitude: CLLocationDegrees = -9.229723
let lanDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span = MKCoordinateSpan(latitudeDelta: lanDelta, longitudeDelta: lonDelta)
let coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region = MKCoordinateRegion(center: coordinates, span: span)
map.setRegion(region, animated: true)
let lpgr = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.longpress(gestureRecognizer:)))
lpgr.minimumPressDuration = 0.5
map.addGestureRecognizer(lpgr)
}
func longpress(gestureRecognizer: UIGestureRecognizer) {
let touchPoint = gestureRecognizer.location(in: self.map)
let coordinate = map.convert(touchPoint, toCoordinateFrom: self.map)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "My Place"
map.addAnnotation(annotation)
print("longpress activated")
numberOfLongPress = numberOfLongPress + 1
print(numberOfLongPress) //detect number of long presses
}
}

Selector is called on every state change, so you better check the state and do what you need to do on .began or .ended.
func longpress(gestureRecognizer: UIGestureRecognizer) {
guard gestureRecognizer.state == .began else { return }
// add annotation
}

Let print the state of gestureRecognizer, you'll see .began and .ended. So let check the state of gestureRecognizer before add an annotation.
func longpress(gestureRecognizer: UILongPressGestureRecognizer) {
if gestureRecognizer.state == began {
// do something here
}
}

Related

swift passing data in textfield to mapview

I'm making an app to search the location. It contains a VC(viewcontroller) to show the searching record.
Q1. How to get the input in textfield and show the location in the mapview?
Q2. I can't unwind the viewcontroller. I've done the code and wire in the storyboard. But still do nothing.
Q3. How to pass the data in the textfield in VC A to the tableview in VC B when a button is clicked?
Or am I do it wrong?
The code:
#IBAction func unwind(segue: UIStoryboardSegue) {
viewController?.dismiss(animated: true)
self.performSegue(withIdentifier: "unwindToMap", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
//Check for Location Services
if (CLLocationManager.locationServicesEnabled()) {
myLocationManager = CLLocationManager()
myLocationManager.delegate = mapViewDelegate as? CLLocationManagerDelegate
myLocationManager.desiredAccuracy = kCLLocationAccuracyBest
myLocationManager.requestAlwaysAuthorization()
myLocationManager.requestWhenInUseAuthorization()
}
mapViewDelegate = searchBox.text as? MKMapViewDelegate
}
#IBAction func searchButtonClick(_ sender: Any) {
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let span = MKCoordinateSpanMake(0.005, 0.005)
let region = MKCoordinateRegion(center: location, span: span)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = searchBox.text!
/*
//find the words
let _ : UITextPosition = searchBox.beginningOfDocument
let _ : UITextPosition = searchBox.endOfDocument
let _ : UITextRange = searchBox.selectedTextRange!
// Get cursor position
if let selectedRange = searchBox.selectedTextRange {
_ = searchBox.offset(from: searchBox.beginningOfDocument, to: selectedRange.start)
}
searchBox.selectedTextRange = searchBox.textRange(
from: searchBox.beginningOfDocument, to: searchBox.endOfDocument)
*/
// myMapView.add(location as! MKOverlay)
myMapView.addAnnotation(annotation)
myMapView.setRegion(region, animated: true)
}
If your answer is useful, I'll be very appreciated.
Edit:
(24 July 2019)Any more better answers? Although I've reproduce the problem.
It's welcomed that more answers for your reference.
For current location you can use the below code:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
let center = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
// Drop a pin at user's Current Location
let myAnnotation: MKPointAnnotation = MKPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(userLocation.coordinate.latitude, userLocation.coordinate.longitude);
myAnnotation.title = "Current location"
mapView.addAnnotation(myAnnotation)
}
To pass Data from one screen to another you can make use of Structs
Like:
struct Manager
{
static var tfText : String = ""
}
To Update its content From anywhere use
Manager.tfText = textField.text!
To retrive data Same as Above
let variable : String = Manager.tfText
you can make use of navigation controller to pop controller, if need to use unwind did you connected your source vc to unwind segue in storyboard

How to re render MKMapView to update overlay according to new coordinates

I've written this function to add overlay at Loading View and its working perfectly.
How can I use this function or any other method to update overlay according to new coordinates I pass on a button click.
I'm passing an array of type CLLocationCoordinate2D of coordinates.
func drawpolyline(loclist: Array<CLLocationCoordinate2D>) {
// if let overlays = self.mainMapView?.overlays {
// self.mainMapView.removeOverlays(overlays)
// }
if (loclist != nil && loclist.count > 1) {
let startmark = loclist.first
let cord1 = CLLocationCoordinate2D(latitude: (startmark?.latitude)!, longitude: (startmark?.longitude)!)
let polyline = MKPolyline(coordinates: loclist, count: loclist.count)
print(polyline.pointCount)
let latDelta:CLLocationDegrees = 0.02
let lonDelta:CLLocationDegrees = 0.02
let mapSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let mapRegion = MKCoordinateRegion(center: cord1, span: mapSpan)
// let adjustedRegion: MKCoordinateRegion = [self.mainMapView regionThatFits:viewRegion]
if (mainMapView != nil) {
self.mainMapView.delegate = self
self.mainMapView.add(polyline)
self.mainMapView.setRegion(mapRegion, animated: true);
// self.mainMapView.addAnnotation(mapAnnotation);
}
} else {
configureMap()
}
}
//function of MKMapViewDelegate for making overlays
//Function to give polyline color and width etc.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
//Return an `MKPolylineRenderer` for the `MKPolyline` in the `MKMapViewDelegate`s method
if let polyline = overlay as? MKPolyline {
let testlineRenderer = MKPolylineRenderer(polyline: polyline)
testlineRenderer.strokeColor = .blue
testlineRenderer.lineWidth = 3.0
return testlineRenderer
}
fatalError("Something wrong...")
return MKOverlayRenderer()
}
I assume you have a button set up, and linked with IBAction.
You need to call the drawpolyline function from the button action giving the new coordinates in the same format as you have previously when calling it on launch but for your new location.
If you don't understand, maybe provide some more information on how you call the drawpolyline function on launch, I assume in the viewDidLoad.
If you don't know how to call a button use
#IBAction func showListPressed(_ sender: UIButton) {
// call drawpolyline function here with new coordinates
}

Swift MapView Stuck Around User's Current Location

Currently, my code drops a pin on the user's current location. There is one small problem when I try to move the map around, because the view will shift back and be centered around that current location pin. I want the user be able to navigate the map and move it around, and if the user switches view controllers (goes to another tab) and comes back, the map will be centered around the user location pin. I have been trying to modify this code to do this, but I have not had any luck where to start.
import UIKit
import MapKit
import CoreLocation
let newPin = MKPointAnnotation()
class MapVC: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// User's location
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if #available(iOS 8.0, *) {
locationManager.requestAlwaysAuthorization()
} else {
// Fallback on earlier versions
}
locationManager.startUpdatingLocation()
// add gesture recognizer
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(MapVC.mapLongPress(_:))) // colon needs to pass through info
longPress.minimumPressDuration = 1.5 // in seconds
//add gesture recognition
map.addGestureRecognizer(longPress)
}
// func called when gesture recognizer detects a long press
func mapLongPress(_ recognizer: UIGestureRecognizer) {
print("A long press has been detected.")
let touchedAt = recognizer.location(in: self.map) // adds the location on the view it was pressed
let touchedAtCoordinate : CLLocationCoordinate2D = map.convert(touchedAt, toCoordinateFrom: self.map) // will get coordinates
let newPin = MKPointAnnotation()
newPin.coordinate = touchedAtCoordinate
map.addAnnotation(newPin)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
map.removeAnnotation(newPin)
let location = locations.last! as CLLocation
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
//set region on the map
map.setRegion(region, animated: true)
newPin.coordinate = location.coordinate
map.addAnnotation(newPin)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Your code has several problems:
You declare a variable newPin at global scope and in mapLongPress(...) you declare a new variable let newPin = ... locally so the global newPin isn't used.
In didUpdateLocations() you first remove the (global) newPin annotation (why??) and set it again at the end of the function. Because the global newPin was never set to anything useful this will never get the desired result.
Furthermore, in didUpdateLocations() you set the map's region and center point to the current location. This is done on every location update, giving weird results when trying to pan the map.
To set center and region when the view appears, try something like that:
class MapVC: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
// class variable for the current location
var lastLocation: CLLocation?
override func viewDidLoad() {
// ...
}
override func viewDidAppear(_ animated: Bool) {
if self.lastLocation != nil {
// set center and region to current location
let center = CLLocationCoordinate2D(latitude: self.lastLocation.coordinate.latitude, longitude: self.lastLocation.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
//set region on the map
map.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.lastLocation = locations.last
}
}

Swift MkMapView Map is always centered around current location

Currently, my code drops a pin on the user's current location. There is one small problem when I try to move the map around, because the view will shift back and be centered around that current location pin. I want the user be able to navigate the map and move it around, and if the user switches view controllers (goes to another tab) and comes back, the map will be centered around the user location pin. I have been trying to modify this code to do this, but I have not had any luck where to start.
import UIKit
import MapKit
import CoreLocation
let newPin = MKPointAnnotation()
class MapVC: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// User's location
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if #available(iOS 8.0, *) {
locationManager.requestAlwaysAuthorization()
} else {
// Fallback on earlier versions
}
locationManager.startUpdatingLocation()
// add gesture recognizer
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(MapVC.mapLongPress(_:))) // colon needs to pass through info
longPress.minimumPressDuration = 1.5 // in seconds
//add gesture recognition
map.addGestureRecognizer(longPress)
}
// func called when gesture recognizer detects a long press
func mapLongPress(_ recognizer: UIGestureRecognizer) {
print("A long press has been detected.")
let touchedAt = recognizer.location(in: self.map) // adds the location on the view it was pressed
let touchedAtCoordinate : CLLocationCoordinate2D = map.convert(touchedAt, toCoordinateFrom: self.map) // will get coordinates
let newPin = MKPointAnnotation()
newPin.coordinate = touchedAtCoordinate
map.addAnnotation(newPin)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
map.removeAnnotation(newPin)
let location = locations.last! as CLLocation
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
//set region on the map
map.setRegion(region, animated: true)
newPin.coordinate = location.coordinate
map.addAnnotation(newPin)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You can use custom location manager class and call the singleton function in didfinishlaunching with option and save latitude and longitude in UserDefault .Set camera position in viewDidLoad for mapView class
1.Make singleton class
var locationShareInstance:locationManagerClass = locationManagerClass()
class locationManagerClass: NSObject, CLLocationManagerDelegate, WebServiceDelegate , UIAlertViewDelegate
{
var locationManager = CLLocationManager()
class func sharedLocationManager() -> locationManagerClass
{
locationShareInstance = locationManagerClass()
return locationShareInstance
}
func startStandardUpdates() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.activityType = .automotiveNavigation
locationManager.distanceFilter = 10
locationManager.pausesLocationUpdatesAutomatically = false
if (Bundle.main.object(forInfoDictionaryKey: "NSLocationWhenInUseUsageDescription") != nil) {
locationManager.requestWhenInUseAuthorization()
}
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// If it's a relatively recent event, turn off updates to save power.
let location: CLLocation = locations.last!
let strLocation = "\(location.coordinate.latitude)"
if strLocation == "" {
}else{
UserDefaults.standard.set("\(location.coordinate.latitude)", forKey: "lat")
UserDefaults.standard.set("\(location.coordinate.longitude)", forKey: "long")
UserDefaults.standard.synchronize()
debugPrint("Spedd: \(location.speed)")
// self.updateLocationToServer()
self.stopStandardUpdate()
}
}
func stopStandardUpdate(){
locationManager.stopUpdatingLocation()
}
//MARK:- WHEN DENIED
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.denied {
NSLog("DENIAL")
UserDefaults.standard.set("\(0.0)", forKey: "lat")
UserDefaults.standard.set("\(0.0)", forKey: "long")
self.generateAlertToNotifyUser()
}
}
func generateAlertToNotifyUser() {
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.notDetermined{
var title: String
title = ""
let message: String = "Location Services are not able to determine your location"
let alertView: UIAlertView = UIAlertView(title: title, message: message, delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "Settings")
alertView.show()
}
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.denied{
var title: String
title = "Location services are off"
let message: String = "To post spots or find near by spots, you must turn on Location Services from Settings"
let alertView: UIAlertView = UIAlertView(title: title, message: message, delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "Settings")
alertView.show()
}
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.notDetermined
{
startStandardUpdates()
}
}
}
Call this functions in didfinishlaunchingwithoption
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let locationManager = locationManagerClass.sharedLocationManager()
locationManager.startStandardUpdates()
}
3.Set camera in viewDidLoad of your class
if UserDefaults.standard.object(forKey: "lat") != nil {
let lat = UserDefaults.standard.object(forKey: "lat") as! String
let long = UserDefaults.standard.object(forKey: "long") as! String
var userLoc = CLLocationCoordinate2D()
userLoc.latitude = CDouble(lat)!
userLoc.longitude = CDouble(long)!
let span = MKCoordinateSpanMake(0.02, 0.02)
let region = MKCoordinateRegion(center: userLoc, span: span)
mapVw.setRegion(region, animated: true)
mapVw.showsUserLocation = true
}

How to send current location to another iPhone user swift?

I want to be able to send my current location to another iPhone user. I can get my current location and continuously update my location to see where I am on the map but how could I send this info to another iPhone user so they can see where i am in real time?
The basic structure of code is:
import UIKit
import MapKit
class ViewController: UIViewController,CLLocationManagerDelegate {
#IBOutlet weak var myMapView: MKMapView!
let myLocMgr = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
myLocMgr.desiredAccuracy = kCLLocationAccuracyBest
myLocMgr.requestWhenInUseAuthorization()
myLocMgr.startUpdatingLocation()
myLocMgr.delegate = self
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// get most recient coordinate
let myCoor = locations[locations.count - 1]
//get lat & long
let myLat = myCoor.coordinate.latitude
let myLong = myCoor.coordinate.longitude
let myCoor2D = CLLocationCoordinate2D(latitude: myLat, longitude: myLong)
//set span
let myLatDelta = 0.05
let myLongDelta = 0.05
let mySpan = MKCoordinateSpan(latitudeDelta: myLatDelta, longitudeDelta: myLongDelta)
let myRegion = MKCoordinateRegion(center: myCoor2D, span: mySpan)
//center map at this region
myMapView.setRegion(myRegion, animated: true)
//add anotation
let myAnno = MKPointAnnotation()
myAnno.coordinate = myCoor2D
myMapView.addAnnotation(myAnno)
}