8

I'm writing an app and I have an embedded mapview to show user's his location. This is my code so far:

class YourCurrentLocation: UIViewController, CLLocationManagerDelegate {

    @IBOutlet weak var mapView: MKMapView!

    var locationManager = CLLocationManager()
    let regionRadius: CLLocationDistance = 1000

    func checkLocationAuthorizationStatus() {
        if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
            mapView.showsUserLocation = true
            centerMapOnLocation(locationManager.location!, map: mapView, radius: regionRadius)
        } else {
            locationManager.requestAlwaysAuthorization() //requestWhenInUseAuthorization()
        }
    }



    func centerMapOnLocation(location: CLLocation, map: MKMapView, radius: CLLocationDistance) {
        let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
            radius * 2.0, radius * 2.0)
        map.setRegion(coordinateRegion, animated: true)
    }


    override func viewDidLoad() {
        super.viewDidLoad()

       // mapView.delegate = self


        if CLLocationManager.locationServicesEnabled()
        {
            //locationManager = CLLocationManager()
            locationManager.delegate = self
            locationManager.requestAlwaysAuthorization()
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
            print("location enabled")
            checkLocationAuthorizationStatus()            
        }
        else
        {
            print("Location service disabled");
        }


        // Do any additional setup after loading the view.
    }

}

I also added the two entries to my plist:

NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription

and also in my xcode I have set to emulate the GPS data on london, UK.

When I run the app - I see the map, but london is not marked. What am I doing wrong?

Btw, I had to comment out this line:

//mapView.delegate = self

in viewDidLoad(), otherwise I had the error:

Cannot assign value of type YourCurrentLocation to type MKMapViewDelegate

and I'm not sure if that's a part of the problem here.

I want to achieve the effect when I display to the user map and a point marked on that map with his location. Can you help me with that?

user3766930
  • 5,629
  • 10
  • 51
  • 104
  • `locationManager.location!` in your code will give you the simulator or device's last location, which could be anything. In my answer I show you how to use a `CLLocationManagerDelegate` to get the actual user location. – paulvs Feb 28 '16 at 16:49
  • Okay, I see... thanks! one last question - what will happen in a situation when the phone is not in GPS range or the location is not yet distinguished as the real one? – user3766930 Feb 28 '16 at 16:53
  • @ user3766930 you could mark the answer as accepted if it did solve your problem. – paulvs Dec 19 '16 at 16:10

1 Answers1

16

The problem with your code is that you're trying to point the map to the user's location when the user gives location permission, you're not waiting for the CoreLocation to give you the actual user location. You need to use the CLLocationManagerDelegate method locationManager(_:didUpdateLocations:) to be notified of when you get the user's actual location, and there you can set the map to point to the user's location.

class ViewController: UIViewController, CLLocationManagerDelegate {

    @IBOutlet var mapView: MKMapView!
    var locationManager: CLLocationManager?

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager = CLLocationManager()
        locationManager!.delegate = self

        if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
            locationManager!.startUpdatingLocation()
        } else {
            locationManager!.requestWhenInUseAuthorization()
        }
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {


        switch status {
        case .NotDetermined:
            print("NotDetermined")
        case .Restricted:
            print("Restricted")
        case .Denied:
            print("Denied")
        case .AuthorizedAlways:
            print("AuthorizedAlways")
        case .AuthorizedWhenInUse:
            print("AuthorizedWhenInUse")
            locationManager!.startUpdatingLocation()
        }
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        let location = locations.first!
        let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 500, 500)
        mapView.setRegion(coordinateRegion, animated: true)
        locationManager?.stopUpdatingLocation()
        locationManager = nil
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("Failed to initialize GPS: ", error.description)
    }
}
paulvs
  • 11,963
  • 3
  • 41
  • 66
  • ok, I see you trimmed my code a lot, aren't the rest of the code lines necessary? – user3766930 Feb 28 '16 at 16:47
  • btw, is there a way of showing a red pin on the map on the user's exact location? so far I can see only exact area.. – user3766930 Feb 28 '16 at 16:57
  • 1
    Setting `kCLLocationAccuracyBest` is not strictly necessary because it's the default value according to the documentation, although there is no harm in setting it. I also updated the checking of `CoreLocation`'s `authorizationStatus`. – paulvs Feb 28 '16 at 17:01
  • Thank you, that's really helpful! one thought though - shouldn't the behavior of `.AuthorizedWhenInUse` and `.AuthorizedAlways` be the same and always call the `locationManager!.startUpdatingLocation()`? And also, the very last thing, I promise :) - is there a way of putting the red pin on the map? – user3766930 Feb 28 '16 at 17:09
  • 1
    Your app should either need the `AuthorizedWhenInUse` permission, or the `AuthorizedAlways` permission, not both. Since the `AuthorizedAlways` is for when you want your app to react to GPS events while it is in background mode, it's probably not what you want. Here's is an excellent snippet of code to add a marker (in Objective-C, but you should be able to translate it to swift pretty easily) http://stackoverflow.com/a/9895537/1305067. – paulvs Feb 28 '16 at 17:33