I try to save a map with the users location to a post, but I get Value of type 'MKMapView?' has no member 'MKMapView' as an error all the time...
The following shows my code but I leave out any background code to the images and labels as everything there works fine, I just include them in here so you know how I save the post informations... Do you know what my error is and how I can solve it?
var takenMap: MKMapView!
#IBAction func postPressed(_ sender: Any) {
if textView.text != "" && takenImage != nil && userLocation.text != "" {
// Create and save a new job
let newJob = Job(text: textView.text, jobImage: takenImage!, addedByUser: (userLabel?.text)!, userImage: UserImage, location: userLocation.text, map: takenMap.MKMapView)
newJob.save()
}
//MARK:- CLLocationManager Delegates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let center = CLLocationCoordinate2D(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
map.setRegion(region, animated: true)
self.map = takenMap
geoCoder.reverseGeocodeLocation(lastLocation) { (placeMarks, error) in
if error == nil {
if let firstLocation = placeMarks?[0] {
self.locationManager.stopUpdatingLocation()
if let cityName = firstLocation.locality,
let street = firstLocation.thoroughfare {
self.scanLocation = "\(street), \(cityName)"
print("This is the current city name", cityName)
print("this is the current street address", street)
self.takenLocation = self.scanLocation!
self.userLocation.text = self.takenLocation
}
}
}
}
}
}
Job.swift:
var map: String?
init(map: String? = nil) {
self.map = map
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
map = value["location"] as? String
}
}
func save() {
let newPostKey = ref.key
// save jobImage
if let imageData = jobImage?.jpegData(compressionQuality: 0.5) {
let storage = Storage.storage().reference().child("jobImages/\(newPostKey)")
storage.putData(imageData).observe(.success, handler: { (snapshot) in
self.downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
let postDictionary = [
"map" : self.map!
] as [String : Any]
self.ref.setValue(postDictionary)
})
}
}
I left out any code for labels or whatever out so the snippet won't be too long
The code takenMap.MKMapView should probably just be takenMap.
Related
I'm using an API to get latitude and longitude coordinates and place them on a map with the name of the place it corresponds to. I'm able to put one place's lat and long coordinates but I'm not too sure how to add all of them to a map. I can't get my head around how to do it. I've tried to use a for loop to do it but I'm too sure on how I would implement it. This is what I've got so far:
func getData() {
let url = "https://www.givefood.org.uk/api/2/foodbanks/"
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { [self] data, response, error in
guard let data = data, error == nil else {
print("Wrong")
return
}
var result: [Info]?
do {
result = try JSONDecoder().decode([Info].self, from: data)
}
catch {
print("Failed to convert: \(error.localizedDescription)")
}
guard let json = result else {
return
}
for each in json {
var each = 0
each += 1
let comp = json[each].lat_lng?.components(separatedBy: ",")
let latString = comp![each]
let lonString = comp![each]
let lat = Double(latString)
let lon = Double(lonString)
let locationPin: CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat!, lon!)
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(51.55573, -0.108312)
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMetres, longitudinalMeters: regionInMetres)
mapView.setRegion(region, animated: true)
let myAn1 = MapPin(title: json[each].name!, locationName: json[each].name!, coordinate: locationPin)
mapView.addAnnotations([myAn1])
}
})
task.resume()
}
Your loop is wrong, each after for is one Info item. The Int index each is pointless and you set it in each iteration to zero so you get always the same coordinate (at index 1).
First of all declare name and lat_lng as non-optional. All records contain both fields.
struct Info : Decodable {
let lat_lng : String
let name : String
}
Second of all for convenience reasons extend CLLocationCoordinate2D to create a coordinate from a string
extension CLLocationCoordinate2D {
init?(string: String) {
let comp = string.components(separatedBy: ",")
guard comp.count == 2, let lat = Double(comp[0]), let lon = Double(comp[1]) else { return nil }
self.init(latitude: lat, longitude: lon )
}
}
Third of all put all good code into the do scope instead of dealing with optionals and set the region once before the loop
func getData() {
let url = "https://www.givefood.org.uk/api/2/foodbanks/"
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { [self] data, response, error in
if let error = error { print(error); return }
do {
let result = try JSONDecoder().decode([Info].self, from: data!)
let location = CLLocationCoordinate2D(latitude: 51.55573, longitude: -0.108312)
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMetres, longitudinalMeters: regionInMetres)
mapView.setRegion(region, animated: true)
var pins = [MapPin]()
for info in result {
if let coordinate = CLLocationCoordinate2D(string: info.lat_lng) {
pins.append(MapPin(title: info.name, locationName: info.name, coordinate: coordinate))
}
}
DispatchQueue.main.async {
self.mapView.addAnnotations(pins)
}
}
catch {
print("Failed to convert: \(error)")
}
})
task.resume()
}
I am using GoogleMaps to draw route. What I want to do is when user travels on that route remove the line which is already travelled(Like Uber does). I guess we can do it with removing the points from the polyline and redraw it. Is it the correct approach?
How can I know that those points are travelled and need to update the path?
1) Create Globle Variable
var demoPolyline = GMSPolyline()
var demoPolylineOLD = GMSPolyline()
// Set Destination Location Cordinates
var destinationLocation = CLLocation(latitude: 23.072837, longitude: 72.516455)
2) Use CLLocationManagerDelegate Method For update current location
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
let originalLoc: String = "\(location.coordinate.latitude),\(location.coordinate.longitude)"
let destiantionLoc: String = "\(destinationLocation.coordinate.latitude),\(destinationLocation.coordinate.longitude)"
let latitudeDiff: Double = Double(location.coordinate.latitude) - Double(destinationLocation.coordinate.latitude)
let longitudeDiff: Double = Double(location.coordinate.longitude) - Double(destinationLocation.coordinate.longitude)
let waypointLatitude = location.coordinate.latitude - latitudeDiff
let waypointLongitude = location.coordinate.longitude - longitudeDiff
getDirectionsChangedPolyLine(origin: originalLoc, destination: destiantionLoc, waypoints: ["\(waypointLatitude),\(waypointLongitude)"], travelMode: nil, completionHandler: nil)
}
3) Create Method For Draw and update Polyline on Google map
func getDirectionsChangedPolyLine(origin: String!, destination: String!, waypoints: Array<String>!, travelMode: AnyObject!, completionHandler: ((_ status: String, _ success: Bool) -> Void)?)
{
DispatchQueue.main.asyncAfter(deadline: .now()) {
if let originLocation = origin {
if let destinationLocation = destination {
var directionsURLString = "https://maps.googleapis.com/maps/api/directions/json?" + "origin=" + originLocation + "&destination=" + destinationLocation
if let routeWaypoints = waypoints {
directionsURLString += "&waypoints=optimize:true"
for waypoint in routeWaypoints {
directionsURLString += "|" + waypoint
}
}
directionsURLString = directionsURLString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
let directionsURL = NSURL(string: directionsURLString)
DispatchQueue.main.async( execute: { () -> Void in
let directionsData = NSData(contentsOf: directionsURL! as URL)
do{
let dictionary: Dictionary<String, AnyObject> = try JSONSerialization.jsonObject(with: directionsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String, AnyObject>
let status = dictionary["status"] as! String
if status == "OK" {
self.selectedRoute = (dictionary["routes"] as! Array<Dictionary<String, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<String, AnyObject>
let route = self.overviewPolyline["points"] as! String
let path: GMSPath = GMSPath(fromEncodedPath: route)!
self.demoPolylineOLD = self.demoPolyline
self.demoPolylineOLD.strokeColor = UIColor.blue
self.demoPolylineOLD.strokeWidth = 3.0
self.demoPolylineOLD.map = self.mapView
self.demoPolyline.map = nil
self.demoPolyline = GMSPolyline(path: path)
self.demoPolyline.map = self.mapView
self.demoPolyline.strokeColor = UIColor.blue
self.demoPolyline.strokeWidth = 3.0
self.demoPolylineOLD.map = nil
} else {
self.getDirectionsChangedPolyLine(origin: origin, destination: destination, waypoints: waypoints, travelMode: travelMode, completionHandler: completionHandler)
}
} catch {
self.getDirectionsChangedPolyLine(origin: origin, destination: destination, waypoints: waypoints, travelMode: travelMode, completionHandler: completionHandler)
}
})
} else {
print("Destination Location Not Found")
}
} else {
print("Origin Location Not Found")
}
}
}
This is working on my live project
I hope this will work for everybody
What I want to do is pass the user location to complete my JSON URL. But the way I do it, it prints "optional(coordinate)" and I just want the coordinate wihtout the optional. I tried to erase ? but it would mark error at the moment to build.
I tried to get the user location in the func locationManager and I try to use them in the func loadGas. Any help could be of use.
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController,MKMapViewDelegate, CLLocationManagerDelegate,UICollectionViewDelegate, UICollectionViewDataSource {
let manager = CLLocationManager()
public let sMAGNA = "magna"
public let sPREMIUM = "premium"
public let sDIESEL = "diesel"
public let MIN_TIME: CLong = 400
private let MIN_DISTANCE: Float = 1000
private var ubicaciones_selected: [Ubicacion] = []
private var ubicaciones_magna: [Ubicacion] = []
private var ubicaciones_premium: [Ubicacion] = []
private var ubicaciones_diesel: [Ubicacion] = []
private let REQUEST_LOCATION = 1
private var latlon: String = ""
private var mType: String = "magna"
var ubicaciones:[Ubicacion] = []
var Ubigaspin = MKPointAnnotation()
#IBAction func MapType(_ sender: Any) {
if mapa.mapType == MKMapType.standard{
mapa.mapType = MKMapType.satellite
} else { mapa.mapType = MKMapType.standard
}
}
#IBOutlet var mapa: MKMapView!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
print(myLocation)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapa.setRegion(region, animated: true)
self.mapa.showsUserLocation = true
manager.stopUpdatingLocation()
}
override func viewDidLoad() {
super.viewDidLoad()
//con esto obtendremos la ubicacion del usuario
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
mapa.showsUserLocation = true
manager.startUpdatingLocation()
//se cargan los pines y las gasolinas
loadGas(tipo: mType)
}
func loadGas(tipo:String){
mType = tipo
var ubicaciones:[Ubicacion] = []
switch tipo {
case sMAGNA:
ubicaciones = ubicaciones_magna
case sPREMIUM:
ubicaciones = ubicaciones_premium
case sDIESEL:
ubicaciones = ubicaciones_diesel
default:
ubicaciones = ubicaciones_magna
}
if ubicaciones.count == 0 {
let lat = String(describing: manager.location?.coordinate.latitude)
let long = String(describing: manager.location?.coordinate.longitude)
let url = URL(string: "http://192.241.214.56/api/ubicacion/?format=json&sub="+lat+","+long)
print (url)
// let url = URL(string: "http://192.241.214.56/api/ubicacion/?format=json&sub=29.08919%2C-110.96133")
// let url = URL(string: "http://192.241.214.56/api/"+tipo+"/?format=json")
URLSession.shared.dataTask(with: url!, completionHandler: {
(data, response, error) in
if(error != nil){
print("error")
}else{
do{
let ubicaciones_json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [[String : AnyObject]]
for ubicacion in ubicaciones_json{
let nombre:String = ubicacion["nombre"] as! String
let direccion:String = ubicacion["direccion"] as! String
let precio_magna:Float = ubicacion["precio_magna"] as! Float
let precio_premium:Float = ubicacion["precio_premium"] as! Float
let precio_diesel:Float = ubicacion["precio_diesel"] as! Float
let ubicacion:String = ubicacion["ubicacion"] as! String
let p = Ubicacion()
p.ubicacion = ubicacion
p.setLatLng()
p.nombre = nombre
p.direccion = direccion
p.precio_magna = precio_magna
p.precio_premium = precio_premium
p.precio_diesel = precio_diesel
ubicaciones.append(p)
}
self.ubicaciones = ubicaciones
OperationQueue.main.addOperation({
self.updatePins(ubicaciones: ubicaciones)
})
}catch let error as NSError{
print(error)
}
}
}).resume()
}else{
self.ubicaciones = ubicaciones
self.updatePins(ubicaciones: ubicaciones)
}
}
Instead of
let lat = String(describing: manager.location?.coordinate.latitude)
you should do something like this:
guard let location = manager.location else {
return
}
let lat = String(format: "%f", location.coordinate.latitude)
SO, I am new to swift and I made the conversion from current Lat and Long to City name and Country, it works fine like that:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if didFindLocation == false
{
didFindLocation = true
locationManager.stopUpdatingLocation()
userLocation = locations[0]
long = userLocation.coordinate.longitude;
lat = userLocation.coordinate.latitude;
print("\(lat),\(long)")
converLocationToCity()
}
}
func converLocationToCity()
{
let geoCoder = CLGeocoder()
userLocation = CLLocation(latitude: self.lat, longitude: self.long)
geoCoder.reverseGeocodeLocation(userLocation, completionHandler:
{
(placemarks, error) -> Void in
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
if let city = placeMark.addressDictionary!["State"] as? String
{
self.city = city as String
} else
{
self.city = ""
}
if let country = placeMark.addressDictionary!["Country"] as? String
{
self.country = country as String
} else
{
self.country = ""
}
self.currentCity.name = ("\(self.city), \(self.country)" as String)
print("\(self.currentCity.name)")
self.fetchWeather.performCurrentWeatherFetch(forSelectedCity: self.currentCity.name)
DispatchQueue.main.async()
{
(self.superview as! UICollectionView).reloadData()
}
})
}
But when the device is set to other language, Russian for example it returns me the City Name and Country in Russian characters, but I need it to be only in english, please anybody some ideas or suggestions? Thank you!
Here is My Solution
While getting the location data i change `UserDefaults.standard.set(["base"], forKey: "AppleLanguages")'
and once I have received the dictionary in English i remove the Object
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
which then sets applelanguage to default value
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
// Call stopUpdatingLocation() to stop listening for location updates,
// other wise this function will be called every time when user location changes.
// manager.stopUpdatingLocation()
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
//location.accessibilityLanguage = "en-US"
UserDefaults.standard.set(["base"], forKey: "AppleLanguages")
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
return
}
print(addressDict)
// Print each key-value pair in a new row
addressDict.forEach { print($0) }
// Print fully formatted address
if let formattedAddress = addressDict["FormattedAddressLines"] as? [String] {
print(formattedAddress.joined(separator: ", "))
}
// Access each element manually
if let locationName = addressDict["Name"] as? String {
print(locationName)
}
if let street = addressDict["Thoroughfare"] as? String {
print(street)
}
var myCity:String = ""
if let city = addressDict["City"] as? String {
print(city)
if(city != "" ){
myCity = city
}
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
var myCountry:String = ""
if let country = addressDict["Country"] as? String {
print(country)
if(country != "" ){
myCountry = country
}
MyGenericFunctions.sharedInstance.saveCountry(country: country)
}
manager.stopUpdatingLocation()
if(myCity != "" && myCountry != "" && self.isCurrLocAPICalled != true){
print("API Called")
self.isCurrLocAPICalled = true
self.callLocationSearch(strCity: myCity, strCountry: myCountry)
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
}
})
//manager.stopUpdatingLocation()
}
These are my two function and i want to transform the information from function to the other without need to declare a variable to store the information because always when i start my app the initial variable is the one that show up
func action(gestureRecognizer: UIGestureRecognizer) {
var touchPoint = gestureRecognizer.locationInView(self.mapView)
var location:CLLocationCoordinate2D = mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView)
var latDelta:CLLocationDegrees = 0.01
var lonDelta:CLLocationDegrees = 0.01
var span:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta:latDelta, longitudeDelta: lonDelta)
var region:MKCoordinateRegion = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
var annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = ""
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var userLocation:CLLocation = locations[0] as! CLLocation
CLGeocoder().reverseGeocodeLocation(userLocation, completionHandler: { (placeMarks, error) -> Void in
if error != nil
{
println("error: \(error)")
}
else {
let place = CLPlacemark(placemark: placeMarks?[0] as! CLPlacemark)
}
})
}