I am in France, and when I try my new func locationManager function, my map view locate me in San Francisco. Any idea?
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
let location = locations[locations.count-1] as CLLocation
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
self.map.setRegion(region, animated: true)
}
I am using the simulator.
The iOS Simulator defaults to US - San Fran, and does not give you an estimate of your Mac's actual location.
However you can simulate movement or even a specific location like so:
Under the Debug menu in the Simulator, the last entry is "Location"; this gives you a sub menu with:
None
Custom Location
Apple Stores
Apple
City Bicycle Ride
City Run
Freeway Drive
Custom Location lets you enter a Lat/Long value.
Bicycle ride, City Run, and Freeway Drive are simulation of a moving location (in Cupertino, of course).
In addition to Woodstock's answer, you can also set a default location in the scheme. It's in Run/Options. There's an option to allow Location simulation and a bunch of standard defaults you can use for testing. For even more option, you can add a GPX file to your project.
Related
I've built a basic app in X-code which shows the user's location and also zooms in automatically to the user's location. I'd like to be able to move around the map since I will have important annotations set up around the user's location, but every time I navigate to a different part of the map I am dragged back to my zoomed-in location. I was advised to "handle the pan gesture on the map and when the map is in pan state then don't zoom. You can control this with a simple bool flag to zoom or not." Please advise on how to fix this issue.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
private let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
//delegates
locationManager.delegate = self
self.mapView.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
self.mapView.showsUserLocation = true
}
//zoom
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
let region = MKCoordinateRegion(center: mapView.userLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
mapView.setRegion(region, animated: true)
}
}
When you set locationManager.startUpdatingLocation() the device's location hardware (GPS/radio receiver, etc) begins the job of actually trying to determine the location. But, it will continue to do that until you tell it to locationManager.stopUpdatingLocation() So your app will receive an ongoing stream of location updates that are all dealt with by your mapView delegate function mapView didUpdate userLocation Each time that method is called you are instructing the mapView to zoom to your user's location and so it is fighting and over-riding any other action the user is trying to perform at the same time, like scrolling, zooming, etc.
The stream of location updates will each have a figure for the uncertainty (.horizontalAccuracy) in the location measurement (+/- 60m or whatever) and each successive update will hopefully have a smaller uncertainty than the previous one but that isn't guaranteed depending on the local physical environment.
A common approach is, within mapView didUpdate userLocation, to keep a copy of the last update and compare it with each successive update and if the new one is more accurate than the previous one, save the new, else discard. Calculate the distance between new and old and when that distance is less than your previously decided minimum (kCLLocationAccuracyBest or 500m, 50m, 10m, whatever) then locationManager.stopUpdatingLocation()
Until you make the decision to stop updating locations, then you should avoid any kind of auto-zooming as the behaviour will frustrate your user.
This is a typical method, but would need modifying for your particular use:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last!
// We've been passed a cached result so ignore and continue.
if newLocation.timestamp.timeIntervalSinceNow < -5 {
return
}
// horizontalAccuracy < 0 indicates invalid result so ignore and continue.
if newLocation.horizontalAccuracy < 0 {
return
}
// Calculate the distance between the new and previous locations.
var distance = CLLocationDistance(Double.greatestFiniteMagnitude)
if let location = location { // location is my previously stored value.
distance = newLocation.distance(from: location)
}
// If newLocation is more accurate than the previous (if previous exists) then use it.
if location == nil || location!.horizontalAccuracy > newLocation.horizontalAccuracy {
lastLocationError = nil
location = newLocation
// When newLocation's accuracy is better than our desired accuracy then stop.
if newLocation.horizontalAccuracy <= locationManager.desiredAccuracy {
stopLocationManager()
}
} else if distance < 5 {
let timeInterval = newLocation.timestamp.timeIntervalSince(location!.timestamp)
if timeInterval > 10 {
stopLocationManager()
}
}
print(newLocation)
}
Sometimes the updates simply never result in an accuracy that satisfies our requirements so there is a timer in there to kill the location hunt after 10 seconds.
I would suggest, that rather than using an automatic zoom to the user's location, that you introduce a button that does the same thing. That way the user knows exactly when and why the behaviour occurs and if they're choosing to zoom to their location they won't be scrolling at the same time.
Not sure if you got your answer yet but here is how I took care of it.
I simply added a var called tracking which is set to true at start.
I called the locationManager.stopUpdatingLocation() inside my viewDidLoad, then set the tracking variable to false.
super.viewDidLoad()
checkLocationServices()
customers = ReadTextFile()
for customer in customers {
addCustomPin(customer:customer)
}
locationManager.stopUpdatingLocation()
tracking = false
Inside my checkLocationServices(), which calls checkLocationAuthorization()
I added
if tracking {
locationManager.startUpdatingLocation()
}
This way, the first time the app calls the checkLocationServices, my tracking var is true, so it checks for the location.
If I wanted to turn the setting back ON, I just need to turn the variable back to true.
I want to set a proper zoom level for the User's current location using the Mapkit . I tried solutions from the stack but not working exactly for me . Can you guys please help me with the thing .
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
for point in points {
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: point["latitude"] as! Double, longitude: point["longitude"] as! Double)
mapView.addAnnotation(annotation)
}
** let region = MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: CLLocationDistance(exactly:9000)!, longitudinalMeters: CLLocationDistance(exactly: 9000)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true) **
I tried this code but its not getting zoomed properly at users location .
Any help is appreciated .
Good evening, I am trying to allow users to locate themselves on a map. When I run the app and access my google maps view, I get the error message "this app has attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an “NSLocationWhenInUseUsageDescription” key with a string value explaining to the user how the app uses this data" and the map is set a default state.
I have put
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs your current location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs your current location</string>
in my info.plist, but it tells me that I need to put in NSLocationWhenInUseUsageDescription in order to access location. I already have put it in and it still won't show the user location! Am I leaving something out or is this a bug? it worked fine for me before I updated to the newest version of Xcode.
My code that enables google maps is as follows,
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.delegate = self
locationManager.stopUpdatingLocation()
let location = locations.last
let lat = (location?.coordinate.latitude)!
let long = (location?.coordinate.longitude)!
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: long, zoom: 17.0)
self.myMapView.animate(to: camera)
showPartyMarkers(lat: lat, long: long)
}
func initGoogleMaps() {
let camera = GMSCameraPosition.camera(withLatitude: 40.014281, longitude: -83.030914, zoom: 17.0)
self.myMapView.camera = camera
self.myMapView.delegate = self
self.myMapView.isMyLocationEnabled = true
}
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
let lat = place.coordinate.latitude
let long = place.coordinate.longitude
showPartyMarkers(lat: lat, long: long)
let camera = GMSCameraPosition.camera(withLatitude: lat, longitude: long, zoom: 17.0)
myMapView.camera = camera
txtFieldSearch.text=place.formattedAddress
Add new records in your new InfoPlist.strings file.
<key>NSLocationAlwaysUsageDescription</key>
<string>app need your location for finding a place</string>
add lines in viewdidload
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
delete your app from simulator and run again.
Add below keys in plist.
Privacy - Location Always and When In Use Usage Description
Privacy - Location When In Use Usage Description
EDIT
I am not seeing Initialisation for locationManager.
Use below code to initialise:
/// CLLocationManager object
var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
return manager
}()
After Init start updating location, for that add below lines in viewDidload
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
I found the answer after reposting the question. If anyone is having this issue as well, what you want to do is click on your projects target build settings, click on info (Left of build settings) and you want to make sure the Custom iOS Target Properties have the same info as your regular p-list. If it does not, simply click the plus sign, and add them in. This should fix the problem because it has with mine. I hope you all find this useful.
I would like to be-able to see if the user is in a 5 km radius of the other location in Swift 4.
For example:
User Location: 37.785834, -122.406417
Other Location: -117.564011, 48.302353
Thanks for anyone who helps!
You can detect when the user enters or leaves a geographic region automatically by using Region Monitoring feature provided by CLLocationManager.
Start monitoring the circle region around the specified coordinate:
let center = CLLocationCoordinate2D(latitude: 37.785834, longitude: -122.406417)
// Make sure the app is authorized.
if CLLocationManager.authorizationStatus() == .authorizedAlways {
// Make sure region monitoring is supported.
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// Register the region.
let maxDistance = locationManager.maximumRegionMonitoringDistance
let region = CLCircularRegion(center: center,
radius: 5000.0, identifier: "YourRegionID")
region.notifyOnEntry = true
region.notifyOnExit = false
locationManager.startMonitoring(for: region)
}
}
Handle a region-related notification (enter notification in this example) by implementing suitable method from CLLocationManagerDelegate:
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
if let region = region as? CLCircularRegion {
// your logic to handle region-entered notification
}
}
I'm trying to build an app that when you open it it zooms to your current location. Below is my code so far.
class MapVC: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
mapView.zoomToUserLocation()
}
}
extension MKMapView {
func zoomToUserLocation() {
print(userLocation.location?.coordinate)
guard let coordinate = userLocation.location?.coordinate else { return }
let region = MKCoordinateRegionMakeWithDistance(coordinate, 10000, 10000)
setRegion(region, animated: true)
}
}
This code does not solve the problem. I believe this is because the app doesn't have time after receiving location authorisation to obtain the user location. I'm looking to write a function that would wait for the app to have a location and then call the zoomToUserLocation function. I've already tried this with a do-while loop which didn't work. I could set a delay but that would mean the zoom was done at a specific time and instead I want the zoom to be done as soon as possible. I've found solutions to this in objective C but couldn't translate it.
This method might be helpful setUserTrackingMode(_:animated:), but I think that's not what you are looking for.
The solution that may fit best for you is to implement locationManager(_:didChangeAuthorization:) and locationManager(_:didUpdateLocations:) on the CLLocationManager's delegate. Then you call zoomToUserLocation() in didUpdateLocations.
Another observation, is that you shouldn't animate the map inside viewDidLoad() 'cause the view it not on screen yet. This should be done in viewDidAppead().
you need both a span and a region...the span allows you to set how far you want to zoom in...
let span = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
if let center = location?.coordinate {
let region = MKCoordinateRegion(center: center, span: span)
self.mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true