Can't get User Location Using MKMapViewDelegate - ios

I have been reading tutorials about getting user location for several hours, yet I cannot seem to get it. I have my ViewController class implementing CLLocationManagerDelegate and MKMapViewDelegate.
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestWhenInUseAuthorization()
self.mapView.delegate = self
self.mapView.showsUserLocation = true
print("Is User Location Visible: \(self.mapView.userLocation.location?.coordinate.latitude)")
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
print("Authorization Status = \(CLLocationManager.authorizationStatus() == .Denied || CLLocationManager.authorizationStatus() == CLAuthorizationStatus.NotDetermined)")
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
else{
print("location services disabled")
}
}
This seems like it would work, as I have set showsUserLocation to true with MKMapView and started updating location services with CLLocationManager.
However, the printout of viewDidLoad() is:
Is User Location Visible: nil
Authorization Status = false
Whenever I try to access user location, it's nil.
I have NSLocationWhenInUseUsageDescription set in Info.plist as well, it successfully asked me if I want to allow location services and I clicked yes.
Thanks
EDIT:
Here is the full code of MapViewController.swift:
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mapView: MKMapView!
let locationManager = CLLocationManager()
override func loadView() {
// create a map view
mapView = MKMapView()
// set the map view we just created as the view for this controller
view = mapView
let ​​​​​​​​​​​​​​​​segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite", "My Location"])
​​​​​​​​​​​​​​​​segmentedControl.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.5)
​​​​​​​​​​​​​​​​segmentedControl.selectedSegmentIndex = 0
​​​​​​​​​​​​​​​​segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), forControlEvents: .ValueChanged)
​​​​​​​​​​​​​​​​segmentedControl.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(​​​​​​​​​​​​​​​​segmentedControl)
let topConstraint = ​​​​​​​​​​​​​​​​segmentedControl.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 8)
let margins = view.layoutMarginsGuide
let leadingConstraint = ​​​​​​​​​​​​​​​​segmentedControl.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor)
let trailingConstraint = ​​​​​​​​​​​​​​​​segmentedControl.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
topConstraint.active = true
leadingConstraint.active = true
trailingConstraint.active = true
}
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestWhenInUseAuthorization()
mapView.delegate = self
mapView.showsUserLocation = true
print("Is User Location Visible: \(mapView.userLocation.location?.coordinate.latitude)")
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
print("Authorization Status = \(CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse)")
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
else{
print("location services disabled")
}
}
func mapTypeChanged(segControl: UISegmentedControl) {
switch segControl.selectedSegmentIndex {
case 0:
mapView.mapType = .Standard
case 1:
mapView.mapType = .Hybrid
case 2:
mapView.mapType = .Satellite
case 3:
zoomOnLocation()
default:
break
}
}
//Zoom to last known location
func zoomOnLocation() {
print("locations = \(self.mapView.userLocation.coordinate.longitude) ")
var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(self.mapView.userLocation.coordinate.latitude, self.mapView.userLocation.coordinate.longitude)
var region:MKCoordinateRegion = MKCoordinateRegionMake(location, MKCoordinateSpanMake(0.5, 0.5))
print("zooming")
mapView.setRegion(region, animated: true)
}
//Delegate if Location has Changed
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
var locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
//Delegate if Authorization Status has Changed
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus){
locationManager.startUpdatingLocation()
}
func mapView(mapView: MKMapView!, didUpdateUserLocation userLocation: MKUserLocation!){
let coord = userLocation.coordinate
print("delegate: coord = \(coord.longitude)")
}
func mapViewDidFinishLoadingMap(mapView: MKMapView!){
print("finished loading mapview")
self.mapView.showsUserLocation = true
print("Is User Location Visible: \(self.mapView.userLocationVisible)")
}
func mapView(mapView: MKMapView!, mapViewWillStartLocatingUser userLocation: MKUserLocation!){
let coord = userLocation.coordinate
print("delegate: coord = \(coord.longitude)")
}
}

Related

Adding search bar and location button to MapKit view controller

This may be an easy solution but I've tried everything it seems. I cannot seem to add a search box and button to my MapKit view controller after my login window. I added my MapKit programatically rather than from the story board and can't seem to add any overlays onto it. Here is the code where I've previously tried to add UIButton and UITextField but it doesn't seem to appear so I've removed it.
I'm trying to get the user to input an address to where they'll get walking directions from their current location with a route poly overlay and also a button for finding their current location, like in the bottom corner of the normal Maps app.
import MapKit
import CoreLocation
class mapViewController: UIViewController, CLLocationManagerDelegate {
let mapView = MKMapView()
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
checkLocationServices()
}
func setupMapView() {
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
func checkLocationAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
followUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
// Show alert
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// Show alert
break
case .authorizedAlways:
break
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocationAuthorization()
} else {
// the user didn't turn it on
}
}
func followUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
}
EDIT: I have managed to add a UISearchController:
let searchController: UISearchController = {
let sc = UISearchController()
sc.obscuresBackgroundDuringPresentation = false
sc.searchBar.placeholder = NSLocalizedString("Search Directions", comment: "")
sc.searchBar.barTintColor = .systemPink
return sc
}()

How to fix Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value?

I have a problem with the Users Location.
When im trying to build the program it gets this error code: (Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value)
My code:
import UIKit
import MapKit
class ViewController: UIViewController {
private let locationManager = CLLocationManager()
private var currentCoordinate: CLLocationCoordinate2D?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
configerLocationServices()
}
private func configerLocationServices() {
locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
locationManager.requestWhenInUseAuthorization()
} else if status == .authorizedAlways || status == .authorizedWhenInUse {
beginLocationUpdates(locationManager: locationManager)
}
}
private func beginLocationUpdates(locationManager: CLLocationManager) {
mapView.showsUserLocation = true //<--- the problem is here
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
private func zoomToLastestLocation(with coordinate: CLLocationCoordinate2D) {
let zoomRegion = MKCoordinateRegion(center: coordinate, latitudinalMeters: 10000, longitudinalMeters: 10000)
mapView.setRegion(zoomRegion, animated: true)
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did get latest Location")
guard let latestLocation = locations.first else { return }
if currentCoordinate == nil {
zoomToLastestLocation(with: latestLocation.coordinate)
}
currentCoordinate = latestLocation.coordinate
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("The Status changed")
if status == .authorizedAlways || status == .authorizedWhenInUse {
self.beginLocationUpdates(locationManager: manager)
}
}
}
I don't know what im doing wrong, has anyone the solution?
Thank you in advance.
private func setNewLoaction(lat:CLLocationDegrees,long:CLLocationDegrees,markerTitle:String){
let center = CLLocationCoordinate2D(latitude: lat, longitude: long)
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: long, zoom: 15)
self.googleMapsView?.camera = camera
self.googleMapsView?.isMyLocationEnabled = true
let marker = GMSMarker(position: center)
marker.map = self.googleMapsView
marker.title = markerTitle
locationManager.stopUpdatingLocation()
}
//MARK:- MAP VIEW
private func setUpMap(){
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
self.googleMapsView = GMSMapView (frame: CGRect(x: 0, y: 0, width: self.view.frame.width-30, height: self.mapView.frame.height))
self.googleMapsView?.settings.compassButton = true
self.googleMapsView?.isMyLocationEnabled = true
self.googleMapsView?.settings.myLocationButton = true
self.mapView.addSubview(self.googleMapsView!)
}
I have called setUpMap in ViewDidload and this setLoaction function in GMSAutocompleteViewControllerDelegate =:
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
destinationField.text = place.formattedAddress
destinationLatitude = place.coordinate.latitude
destinationLongitutude = place.coordinate.longitude
setNewLoaction(lat: destinationLatitude!, long: destinationLongitutude!, markerTitle: "Destination Location")
dismiss(animated: true, completion: nil)
}
you can call this anywhere as per you need, do remember to turn on location when asked for permission and if using in simlulator go to Features/Loaction/custom Location
I have now an other code for the same thing it is shorter than the old one.
Here is the code:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
#IBOutlet var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true // <--- Crash
}
}
And now it gets the same problem as the old one :(

swift user location - update if move

So I have been able to get the users location via the following code.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status != .authorizedWhenInUse {return}
print("test LOCATION BELOW")
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
let locValue: CLLocationCoordinate2D = manager.location!.coordinate
print("UUID: \(String(describing: uuid)) locations = \(locValue.latitude) \(locValue.longitude)")
}
However I want to watch the users location and if they move update their location.
I am wondering how do I get this code to keep checking for the users location?
My
override func viewDidLoad(){
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()}
I get it popping up the request and I approve it but it does not run the code
Here is how you can get the location data. The code is similar to yours but with a bit of change and additions.
First check if you already have user's permission to get their location data.
func isLocationServicesEnabled() -> Bool {
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
return false
case .authorizedAlways, .authorizedWhenInUse:
return true
#unknown default:
return false
}
}
return false
}
If this method returns false you can ask for authorization.
// Initialize manager in your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.delegate = self
// Do other stuff
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Check for auth
if isLocationServicesEnabled() {
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
// Do other stuff
}
Finally in your CLLocationManagerDelegate implementation get the coordinates.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last?.coordinate else { return }
// Use location.latitude and location.longitude here
// If you don't want to receive any more location data then call
locationManager.stopUpdatingLocation()
}
First, you have to check that you have user's permission to get their location data
func checkLocationServicesAuthorization() -> Bool {
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
return false
case .restricted, .denied:
//code for open settings screen in user’s phone
return false
case .authorizedWhenInUse, .authorizedAlways:
return true
default:
return false
}
} else {
//code for open settings screen in user’s phone
}
return false
}
And
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if checkLocationServicesAuthorization() {
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
You can use delegate method didUpdateLocations of CLLocationManagerDelegate and don't forgot to connect delegate yourLocationManager.delegate = self
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.
// yourLocationManager.stopUpdatingLocation()
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
let latitude = String.init(userLocation.coordinate.latitude)
let longitude = String.init(userLocation.coordinate.longitude)
print("\(latitude), \(longitude)")
}

No accuracy when recording CLLocationPoints in Swift

I'm working with a app to record walks, but I can't get the polylines to be as smooth as it looks in other apps, like runkeeper. I have tried with different activityType, desiredAccuracy and so on. I have tested on iPhone 5c, 6s and 7. It always looks like the example photo, it was recorded out in the open without any buildings near. Is there something I'm missing?
This is my viewDidLoad, viewWillAppear, viewDidAppear, locationManager and rendererFor overlay:
import UIKit
import MapKit
import CoreLocation
...
override func viewDidLoad() {
super.viewDidLoad()
myLocations = []
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.activityType = .fitness
manager.distanceFilter = 5
}
override func viewWillAppear(_ animated: Bool) {
//Map
logMap.delegate = self
logMap.mapType = MKMapType.standard
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
manager.allowsBackgroundLocationUpdates = true
logMap.userTrackingMode = MKUserTrackingMode(rawValue: 1)!
}
override func viewDidAppear(_ animated: Bool) {
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined || status == .denied || status ==
.authorizedWhenInUse {
manager.requestAlwaysAuthorization()
manager.requestWhenInUseAuthorization()
manager.requestLocation()
}else{
manager.startUpdatingLocation()
logMap.showsUserLocation = true
}
if status != .denied {
manager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
manager.requestLocation()
}
if status == .authorizedAlways {
logMap.showsUserLocation = true
logMap.mapType = MKMapType(rawValue: 0)!
//logMap.userTrackingMode = MKUserTrackingMode(rawValue: 3)!
manager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations
locations: [CLLocation]){
if(isLoging==true){
if(myLocations.count > 1){
self.distance = self.distance +
Int(locations[0].distance(from: myLocations.last!))
}
myLocations.append(locations[0])
setDistance()
}
// paint line
if (myLocations.count > 1){
let sourceIndex = myLocations.count - 1
let destinationIndex = myLocations.count - 2
let c1 = myLocations[sourceIndex].coordinate
let c2 = myLocations[destinationIndex].coordinate
var a = [c1, c2]
let polyline = MKPolyline(coordinates: &a, count: a.count)
logMap.add(polyline)
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blue
polylineRenderer.lineWidth = 4
return polylineRenderer
}
return MKPolylineRenderer(overlay: overlay)
}
Result:
Try to set distanceFilter to k​CLDistance​Filter​None. This will notify you of every movement, but will consume the most battery.
Even if you are outside of a building you still don't get perfect accuracy.
If I were to guess other fitness app assume that you don't run through buildings and stuff, so they adjust you path to match streets.
I know for a fact that Strava shows my path through buildings and gardens, because they don't get perfect accuracy.
Do this to get best accuracy to get your location everytime.
override func loadView() {
print("loadView called")
// Enable some map settings
map.isMyLocationEnabled = true
map.settings.myLocationButton = true
map.settings.compassButton = true
map.settings.scrollGestures = true
map.settings.zoomGestures = true
map.delegate = self
view = map
}
override func viewDidLoad() {
super.viewDidLoad()
print("ViewDidLoad called")
// Configuring location manager.
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
}
Add Delegate in ViewController: CLLocationManagerDelegate, GMSMapViewDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager function called")
// Fetch current location coordinates
let locValue:CLLocationCoordinate2D = (locationManager.location?.coordinate)!
currentLatitude = locValue.latitude
currentLongitude = locValue.longitude
print("Current Location = \(currentLatitude!), \(currentLongitude!)")
// Zoom to current location
let target = CLLocationCoordinate2D(latitude: currentLatitude!, longitude: currentLongitude!)
map.camera = GMSCameraPosition.camera(withTarget: target, zoom: 17)
locationManager.stopUpdatingLocation()
//DispatchQueue.main.asyncAfter(deadline: .now() + 1.75, execute: {self.webResponse()})
}

iOS swift Location Manager not getting updated properly

I am using CLLocationManager for getting user location. I need to update user location when they move. I am using this code :
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
private var locationManager = CLLocationManager()
lazy var locations = [CLLocation]()
var op:String = ""
#IBOutlet weak var resultTxt: UITextView!
#IBAction func Start(sender: AnyObject) {
startLocationManager()
showResult()
}
override func viewDidLoad() {
super.viewDidLoad()
}
func startLocationManager(){
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates = true
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func startLocationTracking() {
NSLog("startLocationTracking")
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .NotDetermined, .Restricted, .Denied:
print("No access")
self.startLocationManager()
case .AuthorizedAlways, .AuthorizedWhenInUse:
NSLog("authorizationStatus authorized")
}
} else {
print("Location services are not enabled")
self.startLocationManager()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
let howRecent = location.timestamp.timeIntervalSinceNow
if abs(howRecent) < 10 && location.horizontalAccuracy < 20 {
//update distance
self.locations.append(location)
showResult()
}
}
}
func showResult(){
let currentDate = NSDate()
let res:String = "Result LAT : \(self.locations.last?.coordinate.latitude) AND LNG : \(self.locations.last?.coordinate.longitude) Time : \(currentDate.toShortTimeString()) \n "
op += res
self.resultTxt.text = op
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
And I have already added NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription in .plist.
This code works fine for the first time. But when the user moves "didUpdateLocations" is not getting fired.
Can any one please help me, what I am doing wrong?
Try to add these lines, to be able to monitoring significant location changes when user moves:
locationManager.startMonitoringSignificantLocationChanges()
locationManager.distanceFilter = 300 //value is up to you

Resources