See if the user is near a location? Swift 4 - swift

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
}
}

Related

Swift iOS GoogleMaps CLLocationManager didUpdateLocations not updating camera

I'm new to iOS and am building a very simple iOS using swift 5.7.2 and XCode 14.2.
The app basically needs to show a Google Map View as the user's device travels around. I have successfully made the view and enabled location service and the Map now shows my location on the map. The blue icon also tracks my location as I move around.
However, even though the blue icon representing my position moves around, the map itself doesn't move around and that can result in my position being outside of the map if I have travelled far enough.
My UIViewController is:
import UIKit
import AVKit
import GoogleMaps
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var manager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager()
manager?.delegate = self
manager?.desiredAccuracy = kCLLocationAccuracyBest
manager?.requestAlwaysAuthorization()
manager?.startUpdatingLocation()
let currentCoordinate = manager?.location?.coordinate
let viewBounds = self.view.bounds
let screenCenter = CGPoint(x: viewBounds.midX, y: viewBounds.midY)
let camera = GMSCameraPosition.camera(withLatitude: currentCoordinate?.latitude ?? 51.0447, longitude: currentCoordinate?.longitude ?? -114.0719, zoom: 9.0)
let navView = GMSMapView.map(withFrame: CGRect(x: 130, y: 10, width: viewBounds.maxX - 135 * 2, height: viewBounds.maxY - 20), camera: camera)
navView.isMyLocationEnabled = true
self.view.addSubview(navView)
}
}
And the app looks like:
What I think is wrong:
I am not using didUpdateLocations of locationManager to update the camera position as I am not sure what's the proper way of doing it. Throwing this code inside UIViewController doesn't work:
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locValue:CLLocationCoordinate2D = manager.location!.coordinate
print(locValue)
}
What's the proper way of doing it?
Thanks!
The signature is wrong (it's Swift 2 outdated for a long time).
Please have a look at a the documentation, the current signature is
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last!
print(location.coordinate)
}
and the updated locations are in the locations parameter.
And to avoid the optional replace
var manager: CLLocationManager?
with
let manager = CLLocationManager()
and remove manager = CLLocationManager() and the question marks

MapKit auto-zooming back to current location every time I scroll away from current location

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.

Obtaining user location not functioning in Xcode

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.

How to calculate the distance travelled by user from current location in swift 3.0

I am using UImapkit & core location frameworks
How will I get the total polyLine distance & travelled time
this my code
func locationManager(_ manager: CLLocationManager!, didUpdateToLocation newLocation: CLLocation!, fromLocation oldLocation: CLLocation!) {
if let oldLocationNew = oldLocation as CLLocation?{
let oldCoordinates = oldLocationNew.coordinate
let newCoordinates = newLocation.coordinate
var area = [oldCoordinates, newCoordinates]
print(area)
let polyline = MKPolyline(coordinates: &area, count: area.count)
mkMapView.add(polyline)
}
//calculation for location selection for pointing annoation
if (previousLocation as CLLocation?) != nil{
if previousLocation.distance(from: newLocation) > 10 {
addAnnotationsOnMap(newLocation)
previousLocation = newLocation
}
}else{
//case if previous location doesn't exists
addAnnotationsOnMap(newLocation)
previousLocation = newLocation
}
}
You can use CLLocation to calculate the distance between two locations.
i.e
let distance = newLocation.distance(from: oldLocation)
Once you calculated the distance, then you can easily calculate the travel time by using the speed distance time formula
speed = distance / time
since you know the distance and if you assume the speed, you can calculate time taken for the travel as
time = distance / speed
Hope this stuff will help you.

Wrong Location - located in San Francisco and not in France

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.