I am using Corelocation in several views for which I use the following code :
import UIKit
import CoreLocation
class ModelCL: UIViewController, CLLocationManagerDelegate {
let LocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
//CoreLocation
self.LocationManager.delegate = self
self.LocationManager.desiredAccuracy = kCLLocationAccuracyBest
self.LocationManager.requestWhenInUseAuthorization()
self.LocationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {
(placemarks, error) -> Void in
if error != nil {
print("Error")
}
if let pm = placemarks?.first {
self.displayLocationInfo(pm)
}
else {
print("Error : data error")
}
})
}
func displayLocationInfo (placemark: CLPlacemark) {
self.LocationManager.stopUpdatingLocation()
print(placemark.subThoroughfare)
print(placemark.thoroughfare)
print(placemark.locality)
print(placemark.postalCode)
print(placemark.subAdministrativeArea)
print(placemark.administrativeArea)
print(placemark.country)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error :" + error.localizedDescription)
}
}
It works well on each view as I implemented it on each of them. Wouldn't it better to create only one Model Class with this code and call the administrative level needed on each view? I can't figure out how to call it on another view...
Related
The problem iam having is that when i call the getCityName() function in other classes it returns nil because didUpdateLocations needs more time to run, how to solve this Async problem? and How to return the value of CLLocationManager didUpdateLocations in function?
import Foundation
import CoreLocation
class userCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var cityName:String!
init(cityName:String) {
super.init()
setUp()
}
func getCityName() -> String?{
return cityName
}
func setUp() {
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""
manager.stopUpdatingLocation()
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
extension CLLocation {
func geocode(completion: #escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
class userCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var cityName:String!
//1. Add a closure callback
var didGetCity: ((String) -> Void)?
//2. Send City Name when received
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""
// Check for null
// Send city name in the completion block
if (self.didGetCity != nil){
self.didGetCity?(self.cityName)
}
manager.stopUpdatingLocation()
}
}
}
//3. Add code to your class where you need the city Name
var City : String?
let locationManager = userCurrentLocation (cityName: "")
locationManager.didGetCity = {
[weak self] city in
self!.City = city
}
You can use Delegate to tell the main class that everything is ready and use that value
import Foundation
import CoreLocation
protocol userCurrentLocationDelegate: class {
func userCurrentLocationDidGetLocation(_ userLocation: userCurrentLocation)
}
class userCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var cityName:String!
weak var delegate: userCurrentLocationDelegate?
init(cityName:String) {
super.init()
setUp()
}
func getCityName() -> String?{
return cityName
}
func setUp() {
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""
manager.stopUpdatingLocation()
delegate?.userCurrentLocationDidGetLocation(self)
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
extension CLLocation {
func geocode(completion: #escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
and in Main class that calls this one use delegate from this callback to start calling the city name
I am trying to get user current location latitude and longitude by using reverse CLgeocoding.but,i think methods doesn't called. i am not getting any result in console at least.If any one helps me to do this,would be great.Thank in advance.
import UIKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
#IBOutlet weak var lbl2: UILabel!
#IBOutlet weak var lbl1: UILabel!
var str1: Double?
var str2: Double?
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineMyCurrentLocation()
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
//locationManager.startUpdatingHeading()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error) -> Void in
if (error != nil) {
print("ERROR:" + error!.localizedDescription)
return
}
if (placemarks?.count)! > 0 {
let pm = placemarks![0] as CLPlacemark
self.displayLocationInfo(placemark: pm)
} else {
print("Error with data")
}
})
}
func displayLocationInfo(placemark: CLPlacemark) {
// self.locationManager.stopUpdatingLocation()
print(placemark.locality)
print(placemark.postalCode)
print(placemark.administrativeArea)
print(placemark.country)
print(placemark.location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
#IBAction func currentadd(_ sender: Any) {
}
First make sure you have added one of the keys in your Info.plist
NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription.
Second you need to call determineMyCurrentLocation method in your viewDidLoad or viewDidAappear. You can also call determineMyCurrentLocation from any button or event according to your need.
Edit
One more thing i would like to suggest is you should call manager.stopUpdatingLocation() in your didUpdateLocations if you don't want to use location service constantly.
Updated method
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let managerLocation = manager.location{
CLGeocoder().reverseGeocodeLocation(managerLocation) { (placemarks, error) in
if let locations = placemarks ,let location = locations.first {
self.displayLocationInfo(placemark: location)
}else if let error = error{
print("ERROR:" + error.localizedDescription)
}
}
}
}
I want to get the current longitude and latitude of a location using Swift and display them via labels. I tried to do this but nothing displays on the labels.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate{
#IBOutlet weak var longitude: UILabel!
#IBOutlet weak var latitude: UILabel!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
} else {
println("Location services are not enabled");
}
}
// MARK: - CoreLocation Delegate Methods
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
removeLoadingView()
if (error) != nil {
print(error)
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
longitude.text = coord.longitude
latitude.text = coord.latitude
longitude.text = "\(coord.longitude)"
latitude.text = "\(coord.latitude)"
}
}
IMHO, you are over complicating your code when the solution you are looking is pretty simple.
I have done it by using the following code:
First create an instance of CLLocationManager and Request Authorization
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
then check if the user allowed authorization.
var currentLocation: CLLocation!
if
CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways
{
currentLocation = locManager.location
}
to use it just do this
label1.text = "\(currentLocation.coordinate.longitude)"
label2.text = "\(currentLocation.coordinate.latitude)"
Your idea of setting them to the label.text is correct, however the only reason I can think of is that the user is not giving you permission and that is why your current Location data will be nil.
However you would need to debug and tell us that.
Also the CLLocationManagerDelegate is not necessary.
Hopefully this helps. Ask away if you have doubts.
For Swift 3:
First you need to set allowance to receive User's GPS in the info.plist.
Set: NSLocationWhenInUseUsageDescription with a random String.
And/or: NSLocationAlwaysUsageDescription with a random String.
Then:
import UIKit
import MapKit
class ViewController: UIViewController {
var locManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
locManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways){
guard let currentLocation = locManager.location else {
return
}
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
}
Done.
Despite other advice you should use the CLLocationManagerDelegate to safely retrieve a location (without using it you may get null locations when the location manager doesn't have enough time to update). I strongly recommend wrapping the location manager code within a static shared helper (something along these lines):
class Locator: NSObject, CLLocationManagerDelegate {
enum Result <T> {
case .Success(T)
case .Failure(ErrorType)
}
static let shared: Locator = Locator()
typealias Callback = (Result <Locator>) -> Void
var requests: Array <Callback> = Array <Callback>()
var location: CLLocation? { return sharedLocationManager.location }
lazy var sharedLocationManager: CLLocationManager = {
let newLocationmanager = CLLocationManager()
newLocationmanager.delegate = self
// ...
return newLocationmanager
}()
// MARK: - Authorization
class func authorize() { shared.authorize() }
func authorize() { sharedLocationManager.requestWhenInUseAuthorization() }
// MARK: - Helpers
func locate(callback: Callback) {
self.requests.append(callback)
sharedLocationManager.startUpdatingLocation()
}
func reset() {
self.requests = Array <Callback>()
sharedLocationManager.stopUpdatingLocation()
}
// MARK: - Delegate
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
for request in self.requests { request(.Failure(error)) }
self.reset()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: Array <CLLocation>) {
for request in self.requests { request(.Success(self)) }
self.reset()
}
}
Then in view did load (or anywhere else you need to get the current location) run:
Locator.shared.locate { result in
switch result {
case .Success(locator):
if let location = locator.location { /* ... */ }
case .Failure(error):
/* ... */
}
}
In Swift
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
//Labels outlets
#IBOutlet var localityTxtField: UITextField!
#IBOutlet var postalCodeTxtField: UITextField!
#IBOutlet var aAreaTxtField: UITextField!
#IBOutlet var countryTxtField: UITextField!
let locationManager = CLLocationManager()
//View Didload
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//Button Location
#IBAction func findMyLocation(_ sender: AnyObject) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if (error != nil) {
print("Reverse geocoder failed with error" + (error?.localizedDescription)!)
return
}
if (placemarks?.count)! > 0 {
print("placemarks",placemarks!)
let pm = placemarks?[0]
self.displayLocationInfo(pm)
} else {
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(_ placemark: CLPlacemark?) {
if let containsPlacemark = placemark {
print("your location is:-",containsPlacemark)
//stop updating location to save battery life
locationManager.stopUpdatingLocation()
let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
localityTxtField.text = locality
postalCodeTxtField.text = postalCode
aAreaTxtField.text = administrativeArea
countryTxtField.text = country
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while updating location " + error.localizedDescription)
}
}
In current thread a solution was proposed without delegate but in Xcode 9.1 testing in simulator it did not work, location was nil. This code worked:
import UIKit
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last! as CLLocation
/* you can use these values*/
let lat = location.coordinate.latitude
let long = location.coordinate.longitude
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am junior but I solved it in this way:
I have created extension of my class inherited CLLocationManagerDelegate and following steps:
1.Import CoreLocation to your ViewController
import CoreLocation
2.Then initialize location manager and location variables inside your ViewController.
var locManager = CLLocationManager()
var currentUserLocation: CLLocation!
Inside viewDidLoad() request location init delegate and requestUsageDescription
locManager.requestWhenInUseAuthorization()
locManager.delegate = self
locManager.requestLocation()
Then I have just created extension for my viewController inheriting CLLocationManagerDelegate
extension theNameOfYourViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// .requestLocation will only pass one location to the locations array
// hence we can access it by taking the first element of the array
if let location = locations.first {
print(location.coordinate.latitude)
print(location.coordinate.longitude)
}
}
}
Just remember to change the names according your needs also whenever you need location just use the function request location
locManager.requestLocation()
I agree with Kevin above, but if you're looking for less code for something simpler the following will suffice:
Make sure to use the CLLocationManagerDelegate
Swift 4:
In viewDidLoad you can add the following
locationManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) || (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) {
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
And for the first request respond once the user gives or denies permission:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
//Process location information and update.
}
Make sure to add the following keys to Info.plist:
Privacy - Location When In Use Usage Description
Privacy - Location Always and When In Use Usage Description
Create User class:
import Foundation
import CoreLocation
import MapKit
class User: NSObject, ObservableObject {
#Published var position = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
}
}
extension User: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("Location services authorization request")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("User location updated")
print("Updated position: \(locations.first?.coordinate.latitude ?? 00)")
if let location = locations.first {
self.position = location.coordinate
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
}
Update
Swift 5+
Xcode 13+
Add these permission in info plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs your location to show nearby services</string>
Import this in your view controller
import CoreLocation
in viewDidLoad()
override func viewDidLoad() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
Create extension like this
extension RegisterViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let locationSafe = locations.last {
locationManager.stopUpdatingLocation()
let latitude = locationSafe.coordinate.latitude
let longitude = locationSafe.coordinate.longitude
self.currentLatitude = latitude
self.currentLongitude = longitude
print(" Lat \(latitude) , Longitude \(longitude)")
}
if locations.first != nil {
print("location:: \(locations[0])")
}
}
}
Run and check this
So I've been using this tutorial, http://www.veasoftware.com/tutorials/2014/10/18/xcode-6-tutorial-ios-8-current-location-in-swift, to learn how to get some basic location information using the CLLocation class. I got it to successfully run when I declare the delegates and other methods INSIDE the viewDidLoad. Yay!
import UIKit
import CoreLocation
class ProfileScreenViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
#IBOutlet var userInfoLabel: UILabel!
var currentUser = PFUser.currentUser()!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setHidesBackButton(true, animated:true);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func testUserInfoButton(sender: UIButton) {
PFUser.logOut()
var currentUser = PFUser.currentUser()
self.navigationItem.setHidesBackButton(true, animated:true);
self.navigationController?.popToRootViewControllerAnimated(true)
}
#IBAction func getLocationButton(sender: UIButton) {
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println("error " + error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
if error != nil {
println("error" + error.localizedDescription)
}
if placemarks.count > 0 {
let pm = placemarks[0] as CLPlacemark
self.displayLocationInfo(pm)
} else {
println("Error " + error.localizedDescription)
}
})
}
func displayLocationInfo(placemark: CLPlacemark){
locationManager.stopUpdatingLocation()
println(placemark.locality)
println(placemark.postalCode)
println(placemark.administrativeArea)
println(placemark.country)
}
}
http://imgur.com/a/pI1tP
However! When I try and move everything to a button action, everything falls apart. The code is the same, and I have no idea how to fix this. I tried re-typing over and over again. The errors keep telling me things like, "definition conflicts with previous value", or such and such view controller does not have a member name x. I even went to another tutorial, on another site, who essentially wrote the same code but into a button (like exactly what I'm trying to do). But they got it to work.
After fiddling around, I got it down to 3 errors. But the frustrating thing is, it is still the exact same code that I got working before... I feel like I'm missing something here when I transfer code within a button, but I'm still a bit new to Swift/Xcode.
As requested, WORKING code, without errors:
import UIKit
import CoreLocation
class ProfileScreenViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
#IBOutlet var userInfoLabel: UILabel!
var currentUser = PFUser.currentUser()!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setHidesBackButton(true, animated:true);
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println("error " + error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
if error != nil {
println("error" + error.localizedDescription)
}
if placemarks.count > 0 {
let pm = placemarks[0] as CLPlacemark
self.displayLocationInfo(pm)
} else {
println("Error " + error.localizedDescription)
}
})
}
func displayLocationInfo(placemark: CLPlacemark){
locationManager.stopUpdatingLocation()
println(placemark.locality)
println(placemark.postalCode)
println(placemark.administrativeArea)
println(placemark.country)
}
Anyways, I thank you if you can take a look at the code, or perhaps have encountered something like this. It's such a small thing, but after 4 hours, I had to seek help.
Sorry I had to link out my images... Stackoverflow didn't let me post images... Very unhelpful...
Whoops. Apparently, you can't define functions inside an action... Strangely enough I've done it already somewhere else in my code.
import UIKit
import CoreLocation
class ProfileScreenViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
#IBOutlet var userInfoLabel: UILabel!
var currentUser = PFUser.currentUser()!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setHidesBackButton(true, animated:true);
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println("error " + error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
if error != nil {
println("error" + error.localizedDescription)
}
if placemarks.count > 0 {
let pm = placemarks[0] as CLPlacemark
self.displayLocationInfo(pm)
} else {
println("Error " + error.localizedDescription)
}
})
}
func displayLocationInfo(placemark: CLPlacemark){
locationManager.stopUpdatingLocation()
println(placemark.locality)
println(placemark.postalCode)
println(placemark.administrativeArea)
println(placemark.country)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func testUserInfoButton(sender: UIButton) {
PFUser.logOut()
var currentUser = PFUser.currentUser()
self.navigationItem.setHidesBackButton(true, animated:true);
self.navigationController?.popToRootViewControllerAnimated(true)
}
#IBAction func getLocationButton(sender: UIButton) {
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
Had similar problems using Swift 2, I had to change :-
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
to
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
which stops one of your errors.
I want to get the current longitude and latitude of a location using Swift and display them via labels. I tried to do this but nothing displays on the labels.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate{
#IBOutlet weak var longitude: UILabel!
#IBOutlet weak var latitude: UILabel!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
} else {
println("Location services are not enabled");
}
}
// MARK: - CoreLocation Delegate Methods
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
removeLoadingView()
if (error) != nil {
print(error)
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
longitude.text = coord.longitude
latitude.text = coord.latitude
longitude.text = "\(coord.longitude)"
latitude.text = "\(coord.latitude)"
}
}
IMHO, you are over complicating your code when the solution you are looking is pretty simple.
I have done it by using the following code:
First create an instance of CLLocationManager and Request Authorization
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
then check if the user allowed authorization.
var currentLocation: CLLocation!
if
CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways
{
currentLocation = locManager.location
}
to use it just do this
label1.text = "\(currentLocation.coordinate.longitude)"
label2.text = "\(currentLocation.coordinate.latitude)"
Your idea of setting them to the label.text is correct, however the only reason I can think of is that the user is not giving you permission and that is why your current Location data will be nil.
However you would need to debug and tell us that.
Also the CLLocationManagerDelegate is not necessary.
Hopefully this helps. Ask away if you have doubts.
For Swift 3:
First you need to set allowance to receive User's GPS in the info.plist.
Set: NSLocationWhenInUseUsageDescription with a random String.
And/or: NSLocationAlwaysUsageDescription with a random String.
Then:
import UIKit
import MapKit
class ViewController: UIViewController {
var locManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
locManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways){
guard let currentLocation = locManager.location else {
return
}
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
}
Done.
Despite other advice you should use the CLLocationManagerDelegate to safely retrieve a location (without using it you may get null locations when the location manager doesn't have enough time to update). I strongly recommend wrapping the location manager code within a static shared helper (something along these lines):
class Locator: NSObject, CLLocationManagerDelegate {
enum Result <T> {
case .Success(T)
case .Failure(ErrorType)
}
static let shared: Locator = Locator()
typealias Callback = (Result <Locator>) -> Void
var requests: Array <Callback> = Array <Callback>()
var location: CLLocation? { return sharedLocationManager.location }
lazy var sharedLocationManager: CLLocationManager = {
let newLocationmanager = CLLocationManager()
newLocationmanager.delegate = self
// ...
return newLocationmanager
}()
// MARK: - Authorization
class func authorize() { shared.authorize() }
func authorize() { sharedLocationManager.requestWhenInUseAuthorization() }
// MARK: - Helpers
func locate(callback: Callback) {
self.requests.append(callback)
sharedLocationManager.startUpdatingLocation()
}
func reset() {
self.requests = Array <Callback>()
sharedLocationManager.stopUpdatingLocation()
}
// MARK: - Delegate
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
for request in self.requests { request(.Failure(error)) }
self.reset()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: Array <CLLocation>) {
for request in self.requests { request(.Success(self)) }
self.reset()
}
}
Then in view did load (or anywhere else you need to get the current location) run:
Locator.shared.locate { result in
switch result {
case .Success(locator):
if let location = locator.location { /* ... */ }
case .Failure(error):
/* ... */
}
}
In Swift
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
//Labels outlets
#IBOutlet var localityTxtField: UITextField!
#IBOutlet var postalCodeTxtField: UITextField!
#IBOutlet var aAreaTxtField: UITextField!
#IBOutlet var countryTxtField: UITextField!
let locationManager = CLLocationManager()
//View Didload
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//Button Location
#IBAction func findMyLocation(_ sender: AnyObject) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if (error != nil) {
print("Reverse geocoder failed with error" + (error?.localizedDescription)!)
return
}
if (placemarks?.count)! > 0 {
print("placemarks",placemarks!)
let pm = placemarks?[0]
self.displayLocationInfo(pm)
} else {
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(_ placemark: CLPlacemark?) {
if let containsPlacemark = placemark {
print("your location is:-",containsPlacemark)
//stop updating location to save battery life
locationManager.stopUpdatingLocation()
let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
localityTxtField.text = locality
postalCodeTxtField.text = postalCode
aAreaTxtField.text = administrativeArea
countryTxtField.text = country
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while updating location " + error.localizedDescription)
}
}
In current thread a solution was proposed without delegate but in Xcode 9.1 testing in simulator it did not work, location was nil. This code worked:
import UIKit
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last! as CLLocation
/* you can use these values*/
let lat = location.coordinate.latitude
let long = location.coordinate.longitude
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am junior but I solved it in this way:
I have created extension of my class inherited CLLocationManagerDelegate and following steps:
1.Import CoreLocation to your ViewController
import CoreLocation
2.Then initialize location manager and location variables inside your ViewController.
var locManager = CLLocationManager()
var currentUserLocation: CLLocation!
Inside viewDidLoad() request location init delegate and requestUsageDescription
locManager.requestWhenInUseAuthorization()
locManager.delegate = self
locManager.requestLocation()
Then I have just created extension for my viewController inheriting CLLocationManagerDelegate
extension theNameOfYourViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// .requestLocation will only pass one location to the locations array
// hence we can access it by taking the first element of the array
if let location = locations.first {
print(location.coordinate.latitude)
print(location.coordinate.longitude)
}
}
}
Just remember to change the names according your needs also whenever you need location just use the function request location
locManager.requestLocation()
I agree with Kevin above, but if you're looking for less code for something simpler the following will suffice:
Make sure to use the CLLocationManagerDelegate
Swift 4:
In viewDidLoad you can add the following
locationManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) || (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) {
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
And for the first request respond once the user gives or denies permission:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
//Process location information and update.
}
Make sure to add the following keys to Info.plist:
Privacy - Location When In Use Usage Description
Privacy - Location Always and When In Use Usage Description
Create User class:
import Foundation
import CoreLocation
import MapKit
class User: NSObject, ObservableObject {
#Published var position = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
}
}
extension User: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("Location services authorization request")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("User location updated")
print("Updated position: \(locations.first?.coordinate.latitude ?? 00)")
if let location = locations.first {
self.position = location.coordinate
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
}
Update
Swift 5+
Xcode 13+
Add these permission in info plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs your location to show nearby services</string>
Import this in your view controller
import CoreLocation
in viewDidLoad()
override func viewDidLoad() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
Create extension like this
extension RegisterViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let locationSafe = locations.last {
locationManager.stopUpdatingLocation()
let latitude = locationSafe.coordinate.latitude
let longitude = locationSafe.coordinate.longitude
self.currentLatitude = latitude
self.currentLongitude = longitude
print(" Lat \(latitude) , Longitude \(longitude)")
}
if locations.first != nil {
print("location:: \(locations[0])")
}
}
}
Run and check this