I have a class such as:
class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
#Published var lastSeenLocation: CLLocation?
#Published var currentPlacemark: CLPlacemark?
#Published var authorizationStatus: CLAuthorizationStatus
private let locationManager: CLLocationManager
override init() {
locationManager = CLLocationManager()
authorizationStatus = locationManager.authorizationStatus
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func requestPermission() {
locationManager.requestAlwaysAuthorization()
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
authorizationStatus = manager.authorizationStatus
}
}
I'm trying to check if last seen location = cordinates using this code:
let radius: Double = 5 // miles
let userLocation = CLLocation(latitude: locationViewModel.lastSeenLocation?.coordinate.latitude, longitude: locationViewModel.lastSeenLocation?.coordinate.longitude)
let venueLocation = CLLocation(latitude: 51.500909, longitude: -0.177366)
let distanceInMeters = userLocation.distanceFromLocation(venueLocation)
let distanceInMiles = distanceInMeters * 0.00062137
if distanceInMiles < radius {
// user is near the venue
}
The only problem is, that I don't know how to run that code to check constantly. I was thinking .onChange but couldn't figure out how to test for lastSeenlocation in a class. What can I do?
I found a solution here.
For a quick overview:
//I put this code in my LocationViewModel class
func getUserLocation() {
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
print("latitude: \(location.coordinate.latitude), longitude: \(location.coordinate.longitude)")
}
}
//I put this code in my ContentView()
.onAppear {
locationViewModel.getUserLocation()
}
Then it prints your latitude & longitude every time your location changes.
Related
I am trying to get my google maps map to center on my user's location. The user's location updates properly and shows the blue dot indicating their location. However, the camera, also set to the same coordinate locations, instead places itself at (0, 0). I have modified the program below so that it does move to the proper location after it loads. However, since my Bool is a state variable, it gives me a warning that I cannot update State variables during view updates. See the code below:
import SwiftUI
import GoogleMaps
import GoogleMapsUtils
struct GoogleMapsView: UIViewRepresentable {
#ObservedObject var locationManager = LocationManager()
var marker: GMSMarker = GMSMarker()
#Binding var heatmapWeightedData: [GMUWeightedLatLng]
#State var heatmapLayer = GMUHeatmapTileLayer()
#State var isCenteredOnCamera = false
func makeUIView(context: Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: locationManager.latitude, longitude: locationManager.longitude, zoom: 15)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
heatmapLayer.radius = 75
heatmapLayer.weightedData = heatmapWeightedData
heatmapLayer.map = mapView
mapView.isMyLocationEnabled = true
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: UIViewRepresentableContext<GoogleMapsView>) {
if self.isCenteredOnCamera == false {
mapView.animate(to: GMSCameraPosition(latitude: locationManager.latitude, longitude: locationManager.longitude, zoom: 15))
self.isCenteredOnCamera = true //Gives me the error
}
heatmapLayer.weightedData = heatmapWeightedData
heatmapLayer.map = mapView
}
}
Here is the locationManager code:
import Foundation
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
#Published var location: CLLocation? {
willSet { objectWillChange.send() }
}
var latitude: CLLocationDegrees {
return location?.coordinate.latitude ?? 0
}
var longitude: CLLocationDegrees {
return location?.coordinate.longitude ?? 0
}
override init() {
super.init()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager( manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print(status)
}
func locationManager( manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.location = location
}
}
Presumably, the camera is just reading the optional longitude and latitude variables and setting them to 0, for reasons I do not know.
Is there a way for me to update the camera location just once so that it does not infinitely update the camera location? If so, what should I do?
I wrote a program that uses LocationManagerDelegate to display coordinates in the debug area whenever the current location changes. Got an error when retrieving coordinates
Can not use instance member 'locationManager' within property initializer; property initializers run before 'self' is available
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate{
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setUpLocationManager()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setUpLocationManager() {
locationManager = CLLocationManager()
guard let locationManager = locationManager else {return}
locationManager.requestWhenInUseAuthorization()
let status = CLLocationManager.authorizationStatus()
if status == .authorizedWhenInUse {
locationManager.delegate = self
locationManager.distanceFilter = 10
locationManager.startUpdatingLocation()
printLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) ->Optional<Any> {
let location = locations.first
let latitude = location?.coordinate.latitude
let longitude = location?.coordinate.longitude
let latlong = [latitude, longitude]
return latlong
}
let myLocation = locationManager()
func printLocation() {
print("test\(myLocation)")
}
}
test (Function)
is output
let myLocation = locationManager ()
When you change to
let myLocation = locationManager
Your code contains a few mistakes.
The error occurs because you cannot execute the affected line on the top level of the class.
First of all you must not change signatures of delegate methods. This custom delegate method
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) ->Optional<Any> { ...
will never be called.
And apart from that why do you declare the return type as Any? although it's supposed to be [CLLocationCoordinate2D]?
Create the location manager immediately, replace
var locationManager: CLLocationManager!
with
let locationManager = CLLocationManager()
In setUpLocationManager() delete the lines
locationManager = CLLocationManager()
guard let locationManager = locationManager else {return} // this line is completely pointless anyway
printLocation()
The delegate method didUpdateLocations is called periodically and asynchronously. Print the result inside the method
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
let latitude = location.coordinate.latitude
let longitude = location.coordinate.longitude
let latlong = [latitude, longitude]
print("test", latlong)
}
Delete
let myLocation = locationManager()
func printLocation() {
print("test\(myLocation)")
}
I have a problem that a I cannot resolve. I'm trying to get the current location to update the mapview and center the camera, but when I close the view controller and init a new Mapviewcontroller the "current location" show a location incorrect, near to my position but not the current location. Can anyone guide me o help me? this is my code:
class MapContainerViewController: UIViewController{
#IBOutlet weak var mapView: GMSMapView!
var locationManager = CLLocationManager()
var myLocation: CLLocation?
var autorizeChange: Bool?
var geofire: GeoFire?
var createGeoforme: Bool?
var INITIAL_CENTER: CLLocation?
var searchCircle: GMSCircle?
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
autorizeChange = true
checkLocationServices()
createGeoforme = false
mapView.delegate = self
// Do any additional setup after loading the view.
}
func setupLocationManager(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 100
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startMonitoringSignificantLocationChanges()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func checkLocationServices(){
if CLLocationManager.locationServicesEnabled(){
setupLocationManager()
checkLocationAutorization()
} else {
}
}
func checkLocationAutorization(){
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
//Do map stuff
if (mapView.isMyLocationEnabled == false ){
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
var ref: DatabaseReference
ref = Database.database().reference()
geofire = GeoFire(firebaseRef: ref)
}
break
case .denied:
//Show alert instructing
break
case .notDetermined:
//location manager
locationManager.requestWhenInUseAuthorization()
break
case .restricted:
//Show alert letting them know
break
case .authorizedAlways:
break
default:
}
}
}
}
extension MapContainerViewController: GMSMapViewDelegate{
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition)
{
mapView.clear()
let coordinate = mapView.projection.coordinate(for: mapView.center)
var cooordinate2d = CLLocationCoordinate2D.init(latitude: coordinate.latitude, longitude: coordinate.longitude)
var circ = GMSCircle.init(position: coordinate, radius: zoomLevelToRadius(zoomLevel: Double(position.zoom)))
circ.fillColor = UIColor.init(displayP3Red: 0, green: 255, blue: 255, alpha: 0.15)
circ.strokeColor = UIColor.init(displayP3Red: 0, green: 0, blue: 0, alpha: 0.15)
circ.strokeWidth = 0.5
circ.map = mapView;
}
func zoomLevelToRadius(zoomLevel: Double) -> Double{
// Approximation to fit circle into view
return (16384000/pow(2, zoomLevel))
}
}
extension MapContainerViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let location = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
mapView.camera = GMSCameraPosition.camera(withTarget: location, zoom: 14)
let centerLocation = mapView.projection.coordinate(for: mapView.center)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAutorization()
}
}
This sample code can be your solution you can find here https://github.com/SwiftGuides/Google_Place_Picker
This is sample code i wrote for Google place picker to pick exact pin point custom location (not nearby location)
In your case you can apply this too. Just check the example code you will get you solution
App runs in iPhone but when i am trying to run in simulator its shows error as given in image
please help me & thank you in advanced
// THIS IS MY CODE:
locationManager.delegate = self
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized)
{
let latitude1 = locManager.location!.coordinate.latitude.description
latitude = latitude1
let longitude1 = locManager.location!.coordinate.longitude.description
longnitude = longitude1
print(latitude)
print(longnitude)
} else {
latitude = ""
longnitude = ""
}
Try this code: Tested in Xcode simulator(Swift 3)
Update your plist:
Privacy - Location When In Use Usage Description == Some Value
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.mapView.showsUserLocation = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
print(location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Errors: " + error.localizedDescription)
}
}
Output from the code:
You'll have to import the framework as
import CoreLocation
// Here is some more info on the problem = Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var myMap : MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
myMap.showsUserLocation = true }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Errors: " + error.localizedDescription)
}
#IBAction func satelliteView(){
myMap.mapType = MKMapType.Satellite
}
#IBAction func normalView(){
myMap.mapType = MKMapType.Standard
}
#IBAction func pin(sender: UILongPressGestureRecognizer) {
let location = sender.locationInView(self.myMap)
let lCoord = self.myMap.convertPoint(location, toCoordinateFromView: self.myMap)
let anno = MKPointAnnotation()
anno.coordinate = lCoord
anno.title = "store"
anno.subtitle = "loctaion of Store"
self.myMap.removeAnnotations(myMap.annotations)
self.myMap.addAnnotation(anno)
}
}
The simulator does not know your present location. Your need to let the simulator know your location.
In the simulator menu select Debug > Location > Custom Location
You can enter the Lat and Long of your physical location or any other location.
The following is from an app I wrote. You need to call something similar when locationManager(manager:didUpdateLocations: is called
func addPinAtCoordinate(latlong: Vector2D, plotting: Bool = defaultPlotState) {
if self.plotting && !plotting {
self.removeAnnotations(annotationStack)
annotationStack = []
self.plotting = plotting
}
let lat = latlong.x
let lon = latlong.y > 180.0 ? latlong.y - 360.0 : latlong.y
let coord = CLLocationCoordinate2DMake(lat, lon)
let region = self.regionThatFits(MKCoordinateRegionMake(coord, MKCoordinateSpan(latitudeDelta: mapSpanDegrees, longitudeDelta: mapSpanDegrees)))
if region.center.latitude != -180.0 || region.center.longitude != -180.0 {
self.setRegion(region, animated: true)
let note = MKPointAnnotation()
let annotPoint = CLLocationCoordinate2D(latitude: lat, longitude: lon)
note.coordinate = annotPoint
self.addAnnotation(note)
self.annotationStack.append(note)
if annotationStack.count > annotationStackLength {
self.removeAnnotation(annotationStack[0])
annotationStack.removeAtIndex(0)
}
}
else {
NSLog("Can't place invalid cordinate: ", latlong)
}
}
Most specifically useful relative to your problem are the lines creating a region and calling setRegion.