Need help making a MKPointAnnotation a button - swift

i am not sure how to make the MKPointAnnotations a button i want to be able to click on the pop up to send me to another screen
import UIKit
import MapKit
import Firebase
//building the pin ticker
class mapViewController: UIViewController, MKMapViewDelegate{
//class so an image can be added to point annotation
/* class CustomPointAnnotation: MKPointAnnotation{
var imageName: String!
}*/
#IBOutlet weak var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
//set the map location to a specific location when the view loads
let centerLocation = CLLocationCoordinate2DMake(39.863048 , -75.357583)
//logingitude and latitude that the map will cover
let mapSpan = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
//range that the map will show
let mapRange = MKCoordinateRegion(center: centerLocation, span: mapSpan)
//what we will see on the map
self.map.setRegion(mapRange, animated: false)
addAnnotation()
map.delegate = self
let rotate = CGFloat(180)
let regionradius : CLLocationDistance=300.0
let region = MKCoordinateRegion(center: centerLocation, latitudinalMeters: regionradius, longitudinalMeters: regionradius)
//rotation that shows the map is aligned
map.camera.pitch = rotate;
map.setRegion(region, animated: true)
map.delegate = self
map.isUserInteractionEnabled = true
//allows user to still interact with the items on map
let pitch: CGFloat = 300
let heading = 335.0
var camera: MKMapCamera?
camera = MKMapCamera(lookingAtCenter: centerLocation, fromDistance: regionradius, pitch: pitch, heading: heading)
map.camera = camera!
//disables clickables on map
map.isRotateEnabled = false;
map.isZoomEnabled = false;
map.isScrollEnabled = false;
map.showsCompass = false;
}
//funtion to create annotations
private func addAnnotation(){
let parkSpaceOne = MKPointAnnotation()
let parkSpaceTwo = MKPointAnnotation()
let db = Firestore.firestore()
//grabs all the coordinates of the parking spaces in firebase
db.collection("ParkingSpaces").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
if let coords = document.get("coordinate") {
let point = coords as! GeoPoint
let lat = point.latitude
let lon = point.longitude
//string variable for spot field in firebase
let spotpone = document.get("spot") as! String
let spotptwo = document.get("spot") as! String
//if the spot in firbase matches the string then take the coordinates, add them to an annotation and place on the map
if (spotpone == "p1"){
parkSpaceOne.title = "P1"
parkSpaceOne.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
self.map.addAnnotation(parkSpaceOne)
}
else if (spotptwo == "p2"){
parkSpaceTwo.title = "P2"
parkSpaceTwo.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
self.map.addAnnotation(parkSpaceTwo)
}
}
}
}
}
//
}
}

Take a look at the docs
You can detect when the user selects an annotation:
func mapView(MKMapView, didSelect: MKAnnotationView)
Tells the delegate that one of its annotation views was selected.
As well as
func mapView(MKMapView, annotationView: MKAnnotationView, calloutAccessoryControlTapped: UIControl)
Tells the delegate that the user tapped one of the annotation view’s
accessory buttons.

Related

How to replace new location coordinates in current location coordinates in MKMapView in swift

I am using map in Two viewcontrollers
Initially in first viewcontroller i am getting current location in map..
in second viewcontroller i am getting new location coordinates, which i am sending to firstview controller using delegate.. but here how to replace the delegate method coordinates with current location coordinates in first view controller
first view controller code: here in delegate method i am successfully having new location coordinates which i need replace with current location
in userDidEnterInformationdelegate method i am getting all values from 2nd view controller
import UIKit
import CoreLocation
import MapKit
class ProfileAddressViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate, DataEnteredDelegate {
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D()
let locationManager = CLLocationManager()
var latitude: Double?
var logitude: Double?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
}
func userDidEnterInformation(info: DataEnteredModelSave) {
print("map address viewcontroller data \(info)")
self.pincodeField.text = info.pinCode
self.cityField.text = info.cityField
self.latitude = info.zLatitude
self.self.logitude = info.zLongitude
print("new map address viewcontroller data info lat long \(self.latitude) \(self.logitude)")
}
#IBAction func submitButtonClicked(_ sender: UIButton) {
self.view.endEditing(true)
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "NewZoomAddressViewController") as! NewZoomAddressViewController;
self.navigationController?.pushViewController(viewController, animated: true);
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let _: CLLocationCoordinate2D = manager.location?.coordinate else { return }
let userLocation :CLLocation = locations.last! as CLLocation
latitude = userLocation.coordinate.latitude
logitude = userLocation.coordinate.longitude
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print("error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count>0{
let placemark = placemarks![0]
let placemarkDictonary: NSDictionary=placemark.addressDictionary as! NSDictionary
self.pincodeField.text=placemarkDictonary["ZIP"] as? String
self.cityField.text=placemarkDictonary["City"] as? String
}
}
let center = CLLocationCoordinate2D(latitude: latitude!, longitude: logitude!)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
let myAnnotation: MKPointAnnotation = MKPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(userLocation.coordinate.latitude, userLocation.coordinate.longitude);
myAnnotation.title = "Current location"
mapView.addAnnotation(myAnnotation)
}
}
please help me to add delegate method latitude and longitude in locationManager didUpdateLocations
Replace userDidEnterInformation with below code:
func userDidEnterInformation(info: DataEnteredModelSave) {
print("map address viewcontroller data \(info)")
self.pincodeField.text = info.pinCode
self.streetField.text = info.streetField
self.cityField.text = info.cityField
self.latitude = info.zLatitude
self.logitude = info.zLongitude
print("map address viewcontroller data info lat long \(self.latitude) \(self.logitude)")
locationManager.stopUpdatingLocation() //stop updating location when you got data from delegate
let userLocation = CLLocation.init(latitude: latitude!, longitude: logitude!)
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print("error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count>0{
let placemark = placemarks![0]
print(placemark.locality!)
print(placemark.administrativeArea!)
print(placemark.country!)
let placemarkDictonary: NSDictionary=placemark.addressDictionary as! NSDictionary
self.pincodeField.text=placemarkDictonary["ZIP"] as? String
self.cityField.text=placemarkDictonary["City"] as? String
self.plotField.text=placemarkDictonary["Name"] as? String
self.streetField.text=placemarkDictonary["Street"] as? String
self.appormentNoField.text=placemarkDictonary["SubThoroughfare"] as? String
self.colonyField.text=placemarkDictonary["SubLocality"] as? String
self.landmarkField.text=placemarkDictonary["SubThoroughfare"] as? String
}
}
let center = CLLocationCoordinate2D(latitude: latitude!, longitude: logitude!)
//Assign data to map again with new location
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
let myAnnotation: MKPointAnnotation = MKPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(latitude!, logitude!)
myAnnotation.title = "Current location"
mapView.addAnnotation(myAnnotation)
}
I have added comments please check them.
EDIT: As per your second request if you want to show new coordinates on NewZoomAddressViewController first you need to pass coordinates to NewZoomAddressViewController in submitButtonClicked method like:
viewController.latestLocation = CLLocation.init(latitude: self.latitude!, longitude: self.logitude!)
then in NewZoomAddressViewController declare new var
var latestLocation: CLLocation?
and remove other code which is related to user's current location and final code will look like:
import UIKit
import MapKit
import CoreLocation
//import SwiftKeychainWrapper
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: DataEnteredModelSave)
}
class NewZoomAddressViewController: UIViewController {
#IBOutlet weak var oneBtnContainerView: UIView!
var latitudeZoom: Double?
var logitudeZoom: Double?
weak var delegate: DataEnteredDelegate? = nil
var zipName: String?
var localityName: String?
var sublocalityName: String?
var streetNumber: String?
var streetName: String?
let searchCont = UISearchController(searchResultsController: nil)
let annotation = MKPointAnnotation()
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var addressLabel: UILabel!
let regionInMeters: Double = 10000
var previousLocation: CLLocation?
var latestLocation: CLLocation?
override func viewDidLoad() {
super.viewDidLoad()
print("in Zoom map VC")
mapView.delegate = self
addressLabel.text = "\(self.sublocalityName!) \(localityName!) \(self.zipName!)"
centerViewOnUserLocation()
}
#IBAction func backBtn(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden=true
}
var viewController: UIViewController?
#IBAction func confirmBtn(_ sender: Any) {
guard
let zipName = zipName,
let sublocalityName = sublocalityName,
let localityName = localityName,
let lnatZ = latitudeZoom,
let longZ = logitudeZoom
else { return }
let enteredData = DataEnteredModelSave(pinCode: zipName, streetField: sublocalityName, cityField: localityName, zLatitude: lnatZ, zLongitude: longZ)
delegate?.userDidEnterInformation(info: enteredData)
self.navigationController?.popViewController(animated: true)
}
func centerViewOnUserLocation() {
if let location = latestLocation {
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func getCenterLocation(for mapView: MKMapView) -> CLLocation {
latitudeZoom = mapView.centerCoordinate.latitude
logitudeZoom = mapView.centerCoordinate.longitude
print("coordinates from zoom in func \(latitudeZoom), \(logitudeZoom)")
return CLLocation(latitude: latitudeZoom!, longitude: logitudeZoom!)
//print(CLLocation.self)
}
}
extension NewZoomAddressViewController: CLLocationManagerDelegate, UISearchBarDelegate {
// MARK:- Search Address
#IBAction func searchLocationButton(_ sender: Any) {
// let searchCont = UISearchController(searchResultsController: nil)
searchCont.searchBar.delegate = self
searchCont.searchBar.backgroundColor = .blue
present(searchCont, animated:true, completion:nil)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
dismiss(animated: true, completion: nil)
//create the search request
let searchReq = MKLocalSearch.Request()
searchReq.naturalLanguageQuery = searchBar.text
let activeSearch = MKLocalSearch(request: searchReq)
activeSearch.start { (response, error) in
UIApplication.shared.endIgnoringInteractionEvents()
if response == nil{
print("error")
}
else{
//remove annotation
//let annotations = self.mapView.annotations
// self.mapView.removeAnnotation(annotations as! MKAnnotation)
//getting data
let lat = response?.boundingRegion.center.latitude
let long = response?.boundingRegion.center.longitude
//create annotation
//let annotation = MKPointAnnotation()
self.annotation.title = searchBar.text
self.annotation.coordinate = CLLocationCoordinate2DMake(lat!, long!)
self.mapView.addAnnotation(self.annotation)
//zooming annotation
let coordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat!, long!)
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let region = MKCoordinateRegion(center: coordinate, span: span)
self.mapView.setRegion(region, animated: true)
// Add below code to get search address
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: lat!, longitude: long!)
geoCoder.reverseGeocodeLocation(location, completionHandler:
{
placemarks, error -> Void in
// Place details
guard let placeMark = placemarks?.first else { return }
// Location name
self.zipName = placeMark.postalCode
self.localityName = placeMark.locality
self.sublocalityName = placeMark.subLocality
self.streetNumber = placeMark.subThoroughfare
self.streetName = placeMark.thoroughfare
})
}
}
}
}
extension NewZoomAddressViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let center = getCenterLocation(for: mapView)
let geoCoder = CLGeocoder()
guard let previousLocation = self.latestLocation else { return }
guard center.distance(from: previousLocation) > 50 else { return }
self.previousLocation = center
let userLocation :CLLocation = center as CLLocation
latitudeZoom = userLocation.coordinate.latitude
logitudeZoom = userLocation.coordinate.longitude
print("snajxhdwuidhwiuqhdxiqwjmdio \(latitudeZoom), \(logitudeZoom)")
geoCoder.reverseGeocodeLocation(center) { [weak self] (placemarks, error) in
guard let self = self else { return }
if let _ = error {
//TODO: Show alert informing the user
return
}
guard let placemark = placemarks?.first else {
//TODO: Show alert informing the user
return
}
self.streetNumber = placemark.subThoroughfare ?? ""
self.streetName = placemark.thoroughfare ?? ""
print("street number of zoom map \(self.streetName)")
self.localityName = placemark.locality ?? ""//locality
self.sublocalityName = placemark.subLocality ?? ""//locality
self.zipName = placemark.postalCode ?? ""//locality
DispatchQueue.main.async {
self.addressLabel.text = "\(self.streetNumber ?? "") \(self.streetName ?? "") \(self.sublocalityName ?? "") \(self.zipName ?? "") \(self.localityName ?? "")"
print("zzooom map location label \(self.addressLabel.text)")
}
}
}
}

how to make the annotation move over the polyline

i have 3 annotation and i draw polyline between first and second annotation but i need the therd one move over that polyline but it's always move in street polyline to the destnation
-my code
func moveDelivery(_ destinationCoordinate : CLLocationCoordinate2D{
self.deliveryAnnotation.coordinate = CLLocationCoordinate2DMake(29.959640, 31.270421)
let sourcePlaceMark = MKPlacemark(coordinate: self.userAnnotation.coordinate)
//sourcePlaceMark.title
let destPlaceMkark = MKPlacemark(coordinate: self.deliveryAnnotation.coordinate)
let sourceItem = MKMapItem(placemark: sourcePlaceMark)
let destItem = MKMapItem(placemark: destPlaceMkark)
let directionRequest = MKDirections.Request()
directionRequest.source = sourceItem
directionRequest.destination = destItem
directionRequest.transportType = .any
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: {
response, error in
guard let response = response else {
if let error = error {
print(error.localizedDescription)
} else {
self.deliveryAnnotation.courseDegrees = self.getHeadingForDirectionFromCoordinate(self.kitchenAnnotation.coordinate, toLoc: self.userAnnotation.coordinate)
self.view.transform = CGAffineTransform(rotationAngle:CGFloat(self.deliveryAnnotation.courseDegrees))
}
return
}
guard let primaryRoute = response.routes.first else { return }
let route = response.routes[0]
self.mapView.addOverlay(route.polyline, level: .aboveRoads)
let rekt = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegion(rekt), animated: true)
})
//
UIView.animate(withDuration: Double(60), animations: {
self.deliveryAnnotation.coordinate = destinationCoordinate
}, completion: { success in
if success {
}
})
}
Your third annotation isn't following the route because you're animating it moving in a straight line between the first and second line. Try getting the coordinates from the MKRoute's polyline and animate between each one (According to apple's docs MKRoutes are made up of coordinates, but you might be able to use points as well)
If you'd like it to animate over the span of 60 seconds:
func moveDelivery(_ destinationCoordinate: CLLocationCoordinate2D) {
// I don't know why you have the delivery annotation start here, is this for testing?
deliveryAnnotation.coordinate = CLLocationCoordinate2DMake(29.959640, 31.270421)
let sourcePlaceMark = MKPlacemark(coordinate: destinationCoordinate)
let destPlaceMkark = MKPlacemark(coordinate: userAnnotation.coordinate)
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: sourcePlaceMark)
directionRequest.destination = MKMapItem(placemark: destPlaceMkark)
directionRequest.transportType = .any
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: {
response, error in
guard let response = response else {
print("MKRequest gave no response")
if let error = error {
print(error.localizedDescription)
} else {
self.deliveryAnnotation.courseDegrees = self.getHeadingForDirectionFromCoordinate(self.kitchenAnnotation.coordinate, toLoc: self.userAnnotation.coordinate)
self.view.transform = CGAffineTransform(rotationAngle:CGFloat(self.deliveryAnnotation.courseDegrees))
}
return
}
guard let primaryRoute = response.routes.first else {
print("response has no routes")
return
}
self.mapView.addOverlay(primaryRoute.polyline, level: .aboveRoads)
let rekt = primaryRoute.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegion(rekt), animated: true)
let coordinateArray = primaryRoute.polyline.coordinates
assert(coordinateArray.count > 0, "coordinate array is empty")
self.routeCoordinates = coordinateArray
// initiate recursive animations
self.coordinateIndex = 0
})
}
var routeCoordinates = [CLLocationCoordinate2D]()
var avgAnimationTime: Double {
return 60 / Double(routeCoordinates.count)
}
var coordinateIndex: Int! {
didSet {
guard coordinateIndex != routeCoordinates.count else {
print("animated through all coordinates, stopping function")
return
}
animateToNextCoordinate()
}
}
func animateToNextCoordinate() {
let coordinate = routeCoordinates[coordinateIndex]
UIView.animate(withDuration: avgAnimationTime, animations: {
self.deliveryAnnotation.coordinate = coordinate
}, completion: { _ in
self.coordinateIndex += 1
print("moved between coordinates")
})
}
EDIT
make sure to include this extension, otherwise you won't be able to get the coordinates of the MKRoute (source: https://gist.github.com/freak4pc/98c813d8adb8feb8aee3a11d2da1373f)
public extension MKMultiPoint {
var coordinates: [CLLocationCoordinate2D] {
var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
count: pointCount)
getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))
return coords
}
}
EDIT #2
See above, edited original answer to animate through each coordinate after the previous finishes animating. Really rough but it should work.
EDIT #3
Added your code to get the destination variable as well as some assert and debug printing calls. If things aren't working this time, please tell me which debug messages you get.
EDIT #4
I just demo'd my code and it works. Here is the MapViewController class I used along with necessary extensions:
private let reuseId = "deliveryReuseId"
private let userTitle = "user"
private let startingPointTitle = "store"
private let deliveryTitle = "delivery truck"
class MapViewController: UIViewController {
var mapView: MKMapView!
// annotations for this demo, replace with your own annotations
var deliveryAnnotation: MKPointAnnotation = {
let annotation = MKPointAnnotation()
annotation.title = deliveryTitle
return annotation
}()
let userAnnotation: MKPointAnnotation = {
let annotation = MKPointAnnotation()
annotation.title = userTitle
annotation.coordinate = CLLocationCoordinate2DMake(29.956694, 31.276854)
return annotation
}()
let startingPointAnnotation: MKPointAnnotation = {
let annotation = MKPointAnnotation()
annotation.title = startingPointTitle
annotation.coordinate = CLLocationCoordinate2DMake(29.959622, 31.270363)
return annotation
}()
override func viewDidLoad() {
super.viewDidLoad()
loadMapView()
navigate()
}
func loadMapView() {
// set map
mapView = MKMapView()
view = mapView
mapView.delegate = self
mapView.register(MKAnnotationView.self, forAnnotationViewWithReuseIdentifier: reuseId)
// add annotations
mapView.addAnnotation(userAnnotation)
mapView.addAnnotation(startingPointAnnotation)
mapView.addAnnotation(deliveryAnnotation)
}
func navigate() {
let sourcePlaceMark = MKPlacemark(coordinate: startingPointAnnotation.coordinate)
let destPlaceMkark = MKPlacemark(coordinate: userAnnotation.coordinate)
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: sourcePlaceMark)
directionRequest.destination = MKMapItem(placemark: destPlaceMkark)
directionRequest.transportType = .any
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: { response, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let primaryRoute = response!.routes.first else {
print("response has no routes")
return
}
self.mapView.addOverlay(primaryRoute.polyline, level: .aboveRoads)
self.mapView.setRegion(MKCoordinateRegion(primaryRoute.polyline.boundingMapRect), animated: true)
// initiate recursive animation
self.routeCoordinates = primaryRoute.polyline.coordinates
self.coordinateIndex = 0
})
}
var routeCoordinates = [CLLocationCoordinate2D]()
var avgAnimationTime: Double {
// to show delivery in 60 second, replace 60 with amount of seconds you'd like to show
return 60 / Double(routeCoordinates.count)
}
var coordinateIndex: Int! {
didSet {
guard coordinateIndex != routeCoordinates.count else {
print("animated through all coordinates, stopping function")
return
}
animateToNextCoordinate()
}
}
func animateToNextCoordinate() {
let coordinate = routeCoordinates[coordinateIndex]
UIView.animate(withDuration: avgAnimationTime, animations: {
self.deliveryAnnotation.coordinate = coordinate
}, completion: { _ in
self.coordinateIndex += 1
})
}
}
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
// replace these images with your own
switch annotation.title {
case userTitle:
annotationView.image = UIImage(named: "user")
case startingPointTitle:
annotationView.image = UIImage(named: "store")
case deliveryTitle:
annotationView.image = UIImage(named: "deliveryTruck")
default: break
}
return annotationView
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard overlay is MKPolyline else {
return MKOverlayRenderer()
}
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = .black
renderer.lineWidth = 5
renderer.lineJoin = .round
return renderer
}
}
public extension MKMultiPoint {
var coordinates: [CLLocationCoordinate2D] {
var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
count: pointCount)
getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))
return coords
}
}

Swift Parse Saving an Annotation from MapView as a PFGeoPoint

I have an app where I have the user chose a location on MapView where their brand is based. When they tap a location on the map it creates an annotation with a title as well as subtitle. What I'm trying to do now is convert this annotation to a PFGeoPoint when the user hits "next" button so that it can be saved to Parse and later queried and displayed on a map for every user to see.
Here is the code that allows the user to create an annotation for their location:
//Annotations
func longpress(gestureRecognizer: UIGestureRecognizer){
let touchPoint = gestureRecognizer.location(in: self.map)
let coordinate = map.convert(touchPoint, toCoordinateFrom: self.map)
let annotation = MKPointAnnotation()
if isAnnotated == true {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("REMOVED")
isAnnotated = false
} else {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("ANNOTATION ADDED")
isAnnotated = true
}
}
What I need now is to save the annotation as a PFGeoPoint, however I'm not too sure how to go about it. It seems that PFGeoPoint wants the location in the form of CLLocation, but I don't know how to convert the annotation to that. I can provide the code I have so far for saving it to Parse if need be, but it's really scrambled as I don't know how to get the annotation into a format that can be saved to Parse. Any help is much appreciated. Thanks!
EDIT - Here is my code, I now just need a way to save the geoPoint variable that includes the annotation coordinates as a PFGeoPoint under the "next" func
import UIKit
import Parse
import MapKit
import CoreLocation
class BrandLocation: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var activityIndicator = UIActivityIndicatorView()
var isAnnotated = false
#IBOutlet var map: MKMapView!
var locationManager = CLLocationManager()
//QUESTIONABLE
var currentLoc: PFGeoPoint! = PFGeoPoint()
//QUESTIONABLE
var MapViewLocationManager:CLLocationManager! = CLLocationManager()
//ANNOTATION DECLARED
let annotation = MKPointAnnotation()
//Defines geoPoint as nil
var geoPoint : CLLocationCoordinate2D! = nil
func createAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
//THIS IS WHERE PFGEOPOINT IS SAVED
#IBAction func next(_ sender: AnyObject) {
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
let brandLocation = PFObject(className: "location")
//this is the main problem, I'm not sure how to take the coordinates I've defined in the geoPoint variable and now save them as a PFGeoPoint
geoPoint = PFGeoPoint(location: )
geoPoint["brandLocation"] = self.map.annotations
// let geopoint: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
brandLocation["annotationTitle"] = self.map.annotations
brandLocation["annotationSubtitle"] = self.map.annotations
brandLocation.saveInBackground { (succes, error) -> Void in
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
if error != nil {
self.createAlert(title: "Could not update profile", message: "There was a problem updating your profile")
print(":(((")
} else {
self.createAlert(title: "Profile Updated", message: "Profile details successfully updated")
print("MAPPED")
self.performSegue(withIdentifier: "toUserFeed", sender: self)
}
}
// self.performSegue(withIdentifier: "toUserFeed", sender: self)
}
//VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
self.navigationController?.isNavigationBarHidden = true
self.tabBarController?.tabBar.isHidden = true
navigationController?.navigationBar.barTintColor = UIColor.black
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
let uipgr = UITapGestureRecognizer(target: self, action: #selector(BrandLocation.longpress(gestureRecognizer:)))
//uipgr.minimumPressDuration = 1
//uipgr.numberOfTapsRequired = 1
map.addGestureRecognizer(uipgr)
}
override func viewDidAppear(_ animated: Bool) {
}
//Setting up map & location zoom
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let latitude = userLocation.coordinate.latitude
let longitude = userLocation.coordinate.longitude
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region = MKCoordinateRegion(center: location, span: span)
self.map.setRegion(region, animated: true)
locationManager.stopUpdatingLocation()
}
//Annotations
func longpress(gestureRecognizer: UIGestureRecognizer){
let touchPoint = gestureRecognizer.location(in: self.map)
let coordinate = map.convert(touchPoint, toCoordinateFrom: self.map)
//DECLARED ABOVE
//let annotation = MKPointAnnotation()
//WORKING ON GEOPOINT
// currentLoc = PFGeoPoint(location: MapViewLocationManager.location)
//Declares and defines geoPoint
let latitude = annotation.coordinate.latitude
let longitude = annotation.coordinate.longitude
self.geoPoint = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
if isAnnotated == true {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("REMOVED")
isAnnotated = false
} else {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("ANNOTATION ADDED")
isAnnotated = true
}
}
use annotation coordinate to set a geopoint.
let latitude = annotation.coordinate.latitude
let longitude = annotation.coordinate.longitude
let geopoint: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
for you save function
for annotation in self.map.annotations {
//get location of all annotation
let latitude = annotation.coordinate.latitude
let longitude = annotation.coordinate.longitude
let geoPoint = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
//save in Parse object
let brandLocation = PFObject(className: "location")
brandLocation["annotationTitle"] = annotation.annotationTitle
brandLocation["annotationSubtitle"] = annotation.annotationSubtitle
brandLocation["geoPoint"] = geoPoint
brandLocation.saveInBackground { (succes, error) -> Void in
....
}
}

Converting Parse PFGeoPoint into an annotation on the map

I am trying to convert a saved PFGeopoint objects in Parse server into an annotation but i can't seem to figure it out and i don't know what's wrong.
I tried to apply the code from this question : Converting Parse GeoPoint into a CLLocation in Swift but i still couldn't figure it out.
Here is my Code :
import UIKit
import MapKit
import CoreLocation
import Parse
class MapVC: UIViewController, MKMapViewDelegate {
fileprivate let locationManager = CLLocationManager()
fileprivate var startedLoadingPOIs = false
fileprivate var places = [Place]()
var descLocation: PFGeoPoint = PFGeoPoint()
var mapHasCenteredOnce = false
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.requestWhenInUseAuthorization()
mapView.userTrackingMode = MKUserTrackingMode.followWithHeading
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView: MKAnnotationView?
if annotation.isKind(of: MKUserLocation.self) {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "User")
annotationView?.image = UIImage(named: "loc.png")
}
return annotationView
}
}
extension MapVC: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//1
if locations.count > 0 {
let location = locations.last!
print("Accuracy: \(location.horizontalAccuracy)")
//2
if location.horizontalAccuracy < 100 {
//3
manager.stopUpdatingLocation()
let span = MKCoordinateSpan(latitudeDelta: 0.014, longitudeDelta: 0.014)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.region = region
if !startedLoadingPOIs {
startedLoadingPOIs = true
let loader = PlacesLoader()
loader.loadPOIS(location: location, radius: 1000) { placesDict, error in
if let dict = placesDict {
let query = PFQuery(className: "posts")
if let latitude = (PFUser.current()?["location"] as AnyObject).latitude {
if let longitude = (PFUser.current()?["location"] as AnyObject).longitude {
let geoPoint = PFGeoPoint(latitude: latitude, longitude: longitude)
query.whereKey("postLocation", nearGeoPoint: geoPoint, withinKilometers: 1)
}
}
query.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
if error == nil {
for object in objects! {
let pfObject = object as PFObject
let caption = pfObject["title"] as! String
self.descLocation = object["postLocation"] as! PFGeoPoint
let latitude: CLLocationDegrees = self.descLocation.latitude
let longitude: CLLocationDegrees = self.descLocation.longitude
let location = CLLocation(latitude: latitude, longitude: longitude)
let place = Place(location: location, name: caption )
self.places.append(place)
let annotation = PlaceAnnotation(location: place.location!.coordinate, title: place.placeName)
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
}
}
}
}
}
}
}
}
}
}
}
I just had two uanessacry lines that blocked the operation, removed them and it worked for me.
loader.loadPOIS(location: location, radius: 1000) { placesDict, error in
if let dict = placesDict {

Annotating map from array of lat/long gets error when unwrapping (swift)

I've been searching for two days straight and Im stuck on this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
This happens when I try and annotate an array of latitude/Longitude data that am pulling from Firebase.
I am able to use this same data to display in a UITable view successfully, but the problem is when I try and annotate that data to a map.
Goal:Multiple annotations at once. to have each user that is store in Firebase, be annotated on the map with whatever lat/long firebase has for them.
I have read that perhaps I haven't initialized the map view. But I am able to add a single annotation successfully.
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mapView: MKMapView!
var userPinView: MKAnnotationView?
var locationManager: CLLocationManager = CLLocationManager()
var startLocation: CLLocationManager!
var latitude: String?
var longitude: String?
var loc: String?
let cellId = "cellId"
let pinId = "pinId"
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handlelogout))
navigationItem.leftBarButtonItem?.tintColor = UIColor.purple
//navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Cast", style: .plain, target: self, action: #selector( handleStoreUserLocation))
var rightCastBarButtonItem: UIBarButtonItem = UIBarButtonItem(title: "Cast", style: .plain, target: self, action: #selector( handleStoreUserLocation))
var rightWhoIsCastingListBarButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.search, target: self, action: #selector(ViewController.castListTapped))
self.navigationItem.setRightBarButtonItems([rightCastBarButtonItem, rightWhoIsCastingListBarButton], animated: true)
navigationItem.rightBarButtonItem?.tintColor = UIColor.purple
checkIfUserIsLoggedIn()
fetchAllBroadcasts()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
startLocation = nil
}
//this function will grab the current users location and display it as long and lat numbers. Firebase/GEOFire will then need to reference these coordinates when broadcasting location to other users.
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation])
{
let latestLocation: CLLocation = locations[locations.count - 1]
latitude = String(format: "%.4f",
latestLocation.coordinate.latitude)
longitude = String(format: "%.4f",
latestLocation.coordinate.longitude)
// print(latitude, longitude)
}
//this grabs the logged in user Name and displays it in the center nav bar on main screen.
func checkIfUserIsLoggedIn() {
if FIRAuth.auth()?.currentUser?.uid == nil {
perform(#selector(handlelogout), with: nil, afterDelay: 0)
} else {
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("users").child(uid!).observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.navigationItem.title = dictionary ["name"] as? String
}
}, withCancel: nil)
}
//adtional set up after load for MAP
var mapView = MKMapView()
//set map and grab user location
mapView.showsUserLocation = true
// mapView.showAnnotations([MKAnnotation], animated: true)
mapView.mapType = .standard
mapView.frame = view.frame
mapView.tintColor = UIColor.purple
mapView.delegate = self
view.addSubview(mapView)
//******Defalut Map Location********
var location = CLLocationCoordinate2D(
latitude: 49.2810,
longitude: -123.0733
)
//tell the map what the area spanned by the region is
var span = MKCoordinateSpanMake(0.2, 0.2)
//define the region
var region = MKCoordinateRegion(center: location, span: span)
//set region
mapView.setRegion(region, animated: true)
// annotations for the map. this is based of the dedault location above.
// var annotation = MKPointAnnotation()
//annotation.coordinate = location
//annotation.title = "Gayge HQ"
//annotation.subtitle = "oh, hello!"
//mapView.addAnnotation(annotation)
}
//temporary "logout" function . this will later be hidden in slide out menu
func handlelogout (){
do{
try FIRAuth.auth()?.signOut()
} catch let logOutError {
print(logOutError)
}
let loginController = LoginController()
//call the constant loginController that will call the loginController.swift file
present(loginController, animated: true, completion: nil)
}
func handleRightSlideMenu (){
}
func setUpNavBarWithUser (){
}
//*****NEEDS WORK TO FECTH ALL USER LOCATIONS FROM DICTIONARY AND THEN ANNOTATE EACH USER ON THE MAP******
func fetchAllBroadcasts() {
FIRDatabase.database().reference().child("users").observe(.childAdded, with: {(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject?] {
let user = User()
user.setValuesForKeys(dictionary)
self.users.append(user)
//this will crash because of background thread so lets use dispatch_async to fix
DispatchQueue.main.async(){
self.mapView
}
var locationArray: [CLLocationCoordinate2D] = []
var longDouble = CLLocationDegrees(user.long!)
var latDouble = CLLocationDegrees(user.lat!)
var userBroadcastLocations = CLLocationCoordinate2D(
latitude: (latDouble)!, longitude: (longDouble)!) //array of user long and lat
locationArray.append(userBroadcastLocations)
self.userPinView?.annotation
locationArray.append(userBroadcastLocations)
var annotation = MKPointAnnotation()
annotation.coordinate = userBroadcastLocations
annotation.title = "title"
annotation.subtitle = "testing"
//self.mapView.addAnnotations([locationArray.MKPinAnnotation])
self.mapView.showAnnotations([locationArray as! MKAnnotation], animated: true)
}
}, withCancel: nil)
}
//launch a list view of all users broadcasting via seperate viewController (BroadcastListController)
func castListTapped(send: UIButton){
let broadcastListController = BroadcastListController()
let navController = UINavigationController(rootViewController: broadcastListController)
present(navController, animated: true, completion: nil)
}
func handleStoreUserLocation(){
let uid = FIRAuth.auth()?.currentUser?.uid
let ref = FIRDatabase.database().reference().child("users").child(uid!)
let childRef = ref.childByAutoId()
if latitude == nil && longitude == nil {
let values = ["lat": 0, "long": 0]
ref.updateChildValues(values)
}else{
let values = ["lat": latitude, "long": longitude]
ref.updateChildValues(values)
}
}
}
I'll be honest, I've probably read the answer, problem is the answers are so advanced that I can't tell what is applicable to my code or not. Hence this is why I am reaching out for support. Thank you for all you can do.