Can't get coordinates from CLLocationManager in separate variable/constant - ios

I'm trying to set a variable/constant in Swift using a value from a function which finds the user's current location whilst the app is open. When I uncomment the print function it successfully prints the coordinates but I can't access the value outside of the function.
I currently have this as a part of my code which is in the MainVC.swift file as part of the MainVC class:
import UIKit
import MapKit
import CoreLocation
class MainVC: UIViewController, CLLocationManagerDelegate {
//Map
#IBOutlet weak var map: MKMapView!
let manager = CLLocationManager()
let coordinates = locationManager()
//I've also tried using let coordinates = currentCoords
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) -> String {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
map.setRegion(region, animated: true)
lat = String(myLocation.latitude)
long = String(myLocation.longitude)
let currentCoords:String = "\(lat);\(long)"
//print(currentCoords)
self.map.showsUserLocation = true
return currentCoords
}
}
However I'm faced with this error:
"Cannot use instance member 'locationManager' within property initializer; property initializers run before 'self' is available".
When I change the code and use
let coordinates = currentCoords
I get another error showing:
"Use of unresolved identifier 'currentCoords'"
I have also tried using a lazy var and lazy let.

You should just be able to access locationManager.location?.coordinate as in my buttonClick method :
import UIKit
import MapKit
class ViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
let locationManager = CLLocationManager()
#IBAction func buttonClick(_ sender: Any) {
let location = locationManager.location?.coordinate
let lat:Double = (location?.latitude)!
let lon:Double = (location?.longitude)!
print(lat,lon)
}
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
locationManager.delegate = self
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear")
// status is not determined
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
}
// authorization were denied
else if CLLocationManager.authorizationStatus() == .denied {
showAlert("Location services were previously denied. Please enable location services for this app in Settings.")
}
// we do have authorization
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("didUpdateLocations")
print(locations.last?.coordinate)
}
func showAlert(_ title: String) {
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}

This is how you define your public variable
let coordinates:CLLocation = nil
Then in your didUpdateLocations assign the coordinates variable with last element of locations array. Then you'll have access to it everywhere.
Keep in mind didUpdateLocations may be called even up to a few seconds later so your public variable coordinates will be populated after may be up to a few seconds or so.
func coordinatesBecomeAvailable() {
// Here you can print your coordinates or start using them for the first time
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let firstTime:Bool = (coordinates == nil)
coordinates = locations[0]
if (firstTime) // This is first time this function is called
coordinatesBecomeAvailable()
......
< Rest of your code here >
......
< This function does have any return value! no String nothing >
}
You just need to check whether the value of coordinates is nil or not.
Or you can check inside the didUpdateLocations whether this is the first time this delegate method is called.
This is not the most efficient and completed way of initializing and using location manager and I assume you just posted the code related to your problem.

Related

How to use locationManager() in multiple ViewControllers

I need to get the zipCode and the city in multiple viewControllers.
Here is how I'm currently doing it...
import CoreLocation
let locationManager = CLLocationManager()
class MyViewController: UIViewController, CLLocationManagerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)-> Void in
if error != nil {
//AlertView to show the ERROR message
}
if placemarks!.count > 0 {
let placemark = placemarks![0]
self.locationManager.stopUpdatingLocation()
let zipCode = placemark.postalCode ?? ""
let city:String = placemark.locality ?? ""
// Do something with zipCode
// Do something with city
}else{
print("No placemarks found.")
}
})
}
func someFunction() {
locationManager.startUpdatingLocation()
}
Everything works fine but as you can see doing it this way in multiple viewController leads to a lot of code repetition (of course, I'm not showing the whole code).
What would be the most common way to retrieve the zipCode and city from CLLocationManager() in a more practical way from multiple viewControllers?
What I'm thinking is something like...
MyLocationManager.zipCode() // returns zipCode as a string
MyLocationManager.city() // returns city as a string
The usual thing is to have just one location manager in one persistent place that you can always get to from anywhere, like the app delegate or the root view controller.
I tried to implement a singleton CLLocationManager class, I think you can modify the following class to implement some additional methods.
import Foundation
class LocationSingleton: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
private var latitude = 0.0
private var longitude = 0.0
static let shared = LocationSingleton()
private override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
locationManager.requestAlwaysAuthorization() // you might replace this with whenInuse
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
}
}
private func getLatitude() -> CLLocationDegrees {
return latitude
}
private func getLongitude() -> CLLocationDegrees {
return longitude
}
private func zipCode() {
// I think you can figure way out to implemet this method
}
private func city() {
// I think you can figure way out to implemet this method
}
}

Swift 3 - store the User Location and call it from different View Controllers

I'm pretty new in programming and this is my first app, so sorry if the approach is very shabby.
I created a helper method to get the user location, because I need to call it from different view controllers so I thought this was a cleaner way to do it. But I don't know why is not working now (no errors, it just show the general view of Europe). But when it was inside the view controller it worked perfectly fine.
I got this new approach from the course I'm doing and I've been researching in many sources. I've also checked this question but I didn't find any solution yet.
Here is the method I created in the GMSClient file. It will get the user location, but if the user disables this option, it will show the default position (centred in Berlin):
extension GMSClient: CLLocationManagerDelegate {
//MARK: Initial Location: Berlin
func setDefaultInitialLocation(_ map: GMSMapView) {
let camera = GMSCameraPosition.camera(withLatitude: 52.520736, longitude: 13.409423, zoom: 8)
map.camera = camera
let initialLocation = CLLocationCoordinate2DMake(52.520736, 13.409423)
let marker = GMSMarker(position: initialLocation)
marker.title = "Berlin"
marker.map = map
}
//MARK: Get user location
func getUserLocation(_ map: GMSMapView,_ locationManager: CLLocationManager) {
var userLocation: String?
locationManager.requestWhenInUseAuthorization()
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
map.isMyLocationEnabled = true
map.settings.myLocationButton = true
} else {
setDefaultInitialLocation(map)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
map.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
//Store User Location
userLocation = "\(location.coordinate.latitude), \(location.coordinate.longitude)"
print("userLocation is: \((userLocation) ?? "No user Location")")
}
}
}
}
This file has also this singelton:
// MARK: Shared Instance
class func sharedInstance() -> GMSClient {
struct Singleton {
static var sharedInstance = GMSClient()
}
return Singleton.sharedInstance
}
And then I call it in my view controller like this:
class MapViewController: UIViewController, CLLocationManagerDelegate {
// MARK: Outlets
#IBOutlet weak var mapView: GMSMapView!
// MARK: Properties
let locationManager = CLLocationManager()
var userLocation: String?
let locationManagerDelegate = GMSClient()
// MARK: Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = locationManagerDelegate
GMSClient.sharedInstance().getUserLocation(mapView, locationManager)
}
Anyone has an idea of what can be wrong?
Thanks!
Following what Paulw11 said, I found the faster solution using Notifications.
Send notification from the LocationManager delegate method inside the first view Controller:
class MapViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
} else {
initialLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
let userInfo : NSDictionary = ["location" : location]
NotificationCenter.default.post(name: NSNotification.Name("UserLocationNotification"), object: self, userInfo: userInfo as [NSObject : AnyObject])
}
}
}
Set the second view controller as observer. This way I can store the userLocation and use it later for the search request:
class NeighbourhoodPickerViewController: UIViewController, UITextFieldDelegate {
var userLocation: String?
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(locationUpdateNotification), name: Notification.Name("UserLocationNotification"), object: nil)
}
func locationUpdateNotification(notification: NSNotification) {
if let userInfo = notification.userInfo?["location"] as? CLLocation {
self.currentLocation = userInfo
self.userLocation = "\(userInfo.coordinate.latitude), \(userInfo.coordinate.longitude)"
}
}
I guess the problem is here,
self.locationManager.delegate = locationManagerDelegate
You have created a new instance of GMSClient, and saved it in the stored property and that instance is set as the delegate property of CLLocationManager.
You need to do this instead,
self.locationManager.delegate = GMSClient.sharedInstance()
You need to do this because you would want singleton instance of GMSClient to be the delegate for CLLocationManager and not a new instance. That way your singleton class would recieve the callbacks from
CLLocationManager class.
To understand more about why your code was not working, I would suggest you read more about Objects, Instances, Instance variables, Singletons, Delegate design pattern.

User location won't come up in simulator in Xcode

I'm new to app development and I'm trying to get the user location to come up in the simulator but I keep getting the Use of unresolved identifier error. I have looked at other questions, which are very specific to their own projects, and have tried to approach my own app in a similar way but to not avail. Any help, por favor? Here's my code, and a link to a screenshot of Xcode.
import UIKit
import MapKit
import CoreLocation
class SecondViewController: UIViewController, CLLocationManagerDelegate {
//Map
#IBOutlet weak var map: MKMapView!
let manager = CLLocationManager()
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)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
self.map.showsUserLocation = true
}
override func viewDidLoad()
{
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
//This is where I get an error
map.setRegion(region, animated: true)**
}
Error: Use of unresolved identifier
Your let region:MKCoordinateRegion is a local identifier inside your delegate method:
func locationManager(_:didUpdateLocations) {...}
This is why you are getting Use of unresolved identifier. Make this identifier accessible throughout the class, the error will be gone. Like:
class SecondViewController: UIViewController, CLLocationManagerDelegate {
//Map
#IBOutlet weak var map: MKMapView!
var region = MKCoordinateRegion()
.......
.......
}
N.B: But this won't let you see anything in the map. Best way to see any output from your MapView is to put map.setRegion(region, animated: true) inside your delegate method:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.first
let span = MKCoordinateSpanMake(0.01, 0.01)
let myLocation = CLLocationCoordinate2DMake((location?.coordinate.latitude)!, (location?.coordinate.longitude)!)
region = MKCoordinateRegionMake(myLocation, span)
map.setRegion(region, animated: true)
}
Use MKCoordinateRegion instance before viewdidload method
You can follow below code for reference.
class Test: UIViewController {
var region = MKCoordinateRegion()
override func viewDidLoad() {
super.viewDidLoad()
region = MKCoordinateRegionMake(myLocation, span)
}
}
You are getting Error because
the variable region inside viewDidLoad() method do not have access of region variable inside
viewDidLoad() method
//This is where I get an error
map.setRegion(region, animated: true)**
Solution :
Make region variable global to class so that that can be accessed from anywhere you want to.
Instead of this line inside func locationManager
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation,span)
Use
self.region = MKCoordinateRegionMake(myLocation,span)
Here is complete code That you can use.
import UIKit
import MapKit
import CoreLocation
class SecondViewController: UIViewController, CLLocationManagerDelegate {
//Map
#IBOutlet weak var map: MKMapView!
//global variable
var region:MKCoordinateRegion = MKCoordinateRegion()
let manager = CLLocationManager()
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)
self.region = MKCoordinateRegionMake(myLocation, span)
self.map.showsUserLocation = true
}
override func viewDidLoad()
{
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
//This is where I get an error
map.setRegion(region, animated: true)**
}
//
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And more important if you are using simulator then you have to enable location simulation.

Repeating location data with CLLocation

This is a really basic outlay of what I am using...
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
// Call the locationManager class
let LocationManager = CLLocationManager()
// CoreData Delegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// Conform to Delegate Method
self.LocationManager.delegate = self
// Set required accuracy
self.LocationManager.desiredAccuracy = kCLLocationAccuracyBest
// Blue dot
self.mapView.showsUserLocation = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// check location services active
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// check location services
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways:
self.LocationManager.startUpdatingLocation()
case .notDetermined:
self.LocationManager.requestAlwaysAuthorization()
case .authorizedWhenInUse, .restricted, .denied:
let alertController = UIAlertController(
title: "Background Location Access Disabled",
message: "In order to work your location settings need to be set to 'Always'.",
preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
let openAction = UIAlertAction(title: "Open Settings", style: .default) { (action) in
if let url = NSURL(string:UIApplicationOpenSettingsURLString) {
UIApplication.shared.open(url as URL)
}
}
alertController.addAction(openAction)
self.present(alertController, animated: true, completion: nil)
}
}
// Location delegate methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
// get last location
let location = locations.last
print(location!.coordinate.latitude)
// set region
let region = MKCoordinateRegion(center: location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
// deploy region to map
self.mapView.setRegion(region, animated: true)
// Map to follow the user
self.mapView.setUserTrackingMode(MKUserTrackingMode.follow, animated: true)
// Show compass on map
self.mapView.showsCompass = true
// save the location data to CoreData
//self.save(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
// end Location updating
self.LocationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Errors: " + error.localizedDescription)
}
}
My issue is that func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation] calls itself over and over again (around 3 times on initial load)...
I am using the .last which AFAIK is meant to pull out the last result in that object.. which it probably is, as with breakpoints inserted it after the first 2 prints it only returns 1 lot of results...
After searching high and low, I am hoping I can get a result by asking the question... Thanks!
Console output of my issue:
When you call startUpdatingLocation() the location manager immediately starts the delivering of location data. The first incoming locations may be way off your actual location, so check the horizontalAccuracy and verticalAccuracy attributes and dismiss locations which are too inaccurate.
It looks like you just want to get a one-shot location, if so try this code:
// Use:
// at class level:
// var manager: LocationOneShotManager?
// in viewDidLoad:
// manager = LocationOneShotManager()
// manager!.fetchWithCompletion {location, error in
// // fetch location or an error
// if let loc = location {
// println(location)
// } else if let err = error {
// println(err.localizedDescription)
// }
// self.manager = nil
// }
import UIKit
import CoreLocation
// possible errors
enum OneShotLocationManagerErrors: Int {
case AuthorizationDenied
case AuthorizationNotDetermined
case InvalidLocation
}
class LocationOneShotManager: NSObject, CLLocationManagerDelegate {
// location manager
private var locationManager: CLLocationManager?
// destroy the manager
deinit {
locationManager?.delegate = nil
locationManager = nil
}
typealias LocationClosure = ((location: CLLocation?, error: NSError?)->())
private var didComplete: LocationClosure?
// location manager returned, call didcomplete closure
private func _didComplete(location: CLLocation?, error: NSError?) {
locationManager?.stopUpdatingLocation()
didComplete?(location: location, error: error)
locationManager?.delegate = nil
locationManager = nil
}
// location authorization status changed
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .AuthorizedWhenInUse:
self.locationManager!.startUpdatingLocation()
case .Denied:
_didComplete(nil, error: NSError(domain: self.classForCoder.description(),
code: OneShotLocationManagerErrors.AuthorizationDenied.rawValue,
userInfo: nil))
default:
break
}
}
internal func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
_didComplete(nil, error: error)
}
internal func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
_didComplete(location, error: nil)
}
// ask for location permissions, fetch 1 location, and return
func fetchWithCompletion(completion: LocationClosure) {
// store the completion closure
didComplete = completion
// fire the location manager
locationManager = CLLocationManager()
locationManager!.delegate = self
// check for description key and ask permissions
if (NSBundle.mainBundle().objectForInfoDictionaryKey("NSLocationWhenInUseUsageDescription") != nil) {
locationManager!.requestWhenInUseAuthorization()
} else if (NSBundle.mainBundle().objectForInfoDictionaryKey("NSLocationAlwaysUsageDescription") != nil) {
locationManager!.requestAlwaysAuthorization()
} else {
fatalError("To use location in iOS8 you need to define either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in the app bundle's Info.plist file")
}
}
}

Location is working but its not tracking my distrance travelled in Swift 3

I'm making an app for one of my courses. It is supposed to track the distance traveled and update a label to show how far they've gone. When I open the app it asks for permission to track location. The mapView works, it follows the location but the label is never updated to show the distance traveled. I've added my code below, any help is greatly appreciated!
//
// ViewController.swift
// location_tracker
//
// Created by Dale McCaughan on 2016-10-19.
// Copyright © 2016 Dale McCaughan. All rights reserved.
//
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var theMap: MKMapView!
#IBOutlet weak var l: UILabel!
let locationManager = CLLocationManager()
var startLocation: CLLocation!
var monitoredRegions: Dictionary<String, NSDate> = [:]
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
self.navigationController?.isToolbarHidden = false;
//Status bar style and visibility
UIApplication.shared.statusBarStyle = .lightContent
//Change status bar color
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
//if statusBar.respondsToSelector("setBackgroundColor:") {
statusBar.backgroundColor = UIColor.white
//}
UIToolbar.appearance().backgroundColor = UIColor.white
}
override func viewDidLoad() {
super.viewDidLoad()
//Setup the Location Manager
locationManager.delegate = self;
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
//Setup the Map View
theMap.delegate = self
theMap.showsUserLocation = true
theMap.userTrackingMode = .follow
// setup test data
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// status is not determined
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
}
// authorization were denied
else if CLLocationManager.authorizationStatus() == .denied {
showAlert("Location services were previously denied. Please enable location services for this app in Settings.")
}
// we do have authorization
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
locationManager.startUpdatingLocation()
}
}
// MARK: - MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.strokeColor = UIColor.red
circleRenderer.lineWidth = 1.0
return circleRenderer
}
#IBAction func resetDistance(_ sender: AnyObject) {
startLocation = nil
}
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
showAlert("enter \(region.identifier)")
monitoredRegions[region.identifier] = Date() as NSDate?
l.text = "in location manager1"
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
showAlert("exit \(region.identifier)")
monitoredRegions.removeValue(forKey: region.identifier)
l.text = "in location manager2"
}
var lastLocation: CLLocation!
var traveledDistance:Double = 0
func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) {
if let firstLocation = locations.first as? CLLocation
{
theMap.setCenter(firstLocation.coordinate, animated: true)
let region = MKCoordinateRegionMakeWithDistance(firstLocation.coordinate, 1000, 1000)
theMap.setRegion(region, animated: true)
if let oldLocation = lastLocation {
let delta: CLLocationDistance = firstLocation.distance(from: lastLocation)
traveledDistance += delta
}
lastLocation = firstLocation
}
l.text = String(format: "%.3f", traveledDistance/1000) + " kilometers"
}
// MARK: - Helpers
func showAlert(_ title: String) {
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try this:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let firstLocation = locations.first
The delegate signature that you were using wasn't quite what the delegate was looking for. With the signature corrected, you don't need the as CLLocation cast anymore.
I verified that it works with that change. For future reference, you could set a breakpoint at didUpdateLocations and you would see right away that it wasn't getting called and then work backwards from there.
Also make sure that Info.plist contains all the necessary location-related Privacy strings (I usually just put in all three to cover all the bases).

Resources