Cllocationmanager Responsiveness

CLLocationManager responsiveness

OK, a couple of things could improve your lag. First of all, use kCLLocationAccuracyBestForNavigation always. There is no real battery usage difference between that and kCLLocationAccuracyBest, they both use the GPS at top speed. The main difference is in the post-processing that Apple does.

Second, there is no need to filter for speed == 0. Apple already does that filtering: if your speed from the GPS drops below a certain threshold (about 4 km/h), the OS assumes you are standing still, and it substitutes the same location value for all subsequent samples. It does that until it thinks you are moving again. I assume they do that to avoid "jittering" on the map when you are standing still. In fact, speed drops to 0 already for the last real value of a sequence of "standing-still" values, so if you filter on speed == 0 than you are missing one real GPS sample.

Unfortunately, they is no way to avoid that filtering and get real GPS samples. I talked to Apple about it, and their response was that they are not going to change the behaviour. kCLLocationAccuracyBestForNavigation does less aggressive filtering than kCLLocationAccuracyBest, so it's best to use that.

Third, you probably are already doing this, but make sure that you call "setNeedsDisplay" on your view right from the "didUpdateFromLocation:", to make sure that the map is actually redrawn.

If you do all that, you should have a lag of about 1 second. If you want to improve on the 1 second than you can try to use predictive techniques. From the last two locations, and the given speed, you can calculate where the next location is likely to be, and already display that location. I have had mixed results with that. It works well for fast movement that does not change speed suddenly, like driving a car. It works less well for slower movement like walking or biking.

CLLocationManager AuthorizationStatus callback?

You can use the
locationManager:didChangeAuthorizationStatus: CLLocationManagerDelegate method as a "callback" of sorts.

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusDenied) {
// The user denied authorization
}
else if (status == kCLAuthorizationStatusAuthorized) {
// The user accepted authorization
}
}

And in Swift (update suggested by user Michael Marvick, but rejected for some reason...):

func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == CLAuthorizationStatus.denied) {
// The user denied authorization
} else if (status == CLAuthorizationStatus.authorizedAlways) {
// The user accepted authorization
}
}

CLLocationManager always crash and returning nil value

As I said in my comments: Your problem is that CLLocationManager maybe don´t have any position yet, so you are forcing unwrap values that are maybe nil, in didUpdateLocations this will not happen anymore, because this method is called when CLLocationManager have defined position

The main change in your code is

extension LocationViewController : CLLocationManagerDelegate
{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

if let location = locations.last
{
if((location.horizontalAccuracy) < CLLocationAccuracy(0))
{
return
}

lokasiAwal2 = location

//Calling the method when we are sure that a position is getted
self.updateUIAndGetDirection()
self.corLoc.stopUpdatingLocation() //avoiding continue direction changes
}
}
}

Complete Code

import UIKit
import CoreLocation
import MapKit

class LocationViewController: UIViewController {

@IBOutlet weak var mapRoute: MKMapView!
var lokasiAwal2 = CLLocation()
var corLoc = CLLocationManager()

override func viewDidLoad() {
super.viewDidLoad()
mapRoute.delegate = self

//let corLoc2 = CLLocationManager()

corLoc.delegate = self
let statusLoc = CLLocationManager.authorizationStatus()
if statusLoc == .notDetermined{
corLoc.requestWhenInUseAuthorization()
}
corLoc.desiredAccuracy = kCLLocationAccuracyBest

corLoc.startUpdatingLocation()

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

func updateUIAndGetDirection()
{
//let lokasiAwal = CLLocationCoordinate2D(latitude: (corLoc.location?.coordinate.latitude)!, longitude: (corLoc.location?.coordinate.longitude)!)

let lokasiAwal = CLLocationCoordinate2D(latitude: lokasiAwal2.coordinate.latitude, longitude: lokasiAwal2.coordinate.longitude)

//let lokasiAwal = CLLocationCoordinate2D(latitude: -7.263056, longitude: 112.740317)

let lokasiAkhir = CLLocationCoordinate2D(latitude: -7.299356, longitude: 112.676108)
//-7.299356, 112.676108 NH
//-7.289182, 112.676104 PTC
//-7.282713, 112.687633 bandar jakarta
//-7.263056, 112.740317 TP

//placemark
let awalPlaceMark = MKPlacemark(coordinate: lokasiAwal, addressDictionary: nil)
let akhirPlaceMark = MKPlacemark(coordinate: lokasiAkhir, addressDictionary: nil)

let awalMap = MKMapItem(placemark: awalPlaceMark)
let akhirMap = MKMapItem(placemark: akhirPlaceMark)

//anotasi
let awalAnotasi = MKPointAnnotation()
awalAnotasi.title = "Your Location"

//let awalPin = MKPinAnnotationView.init(annotation: awalAnotasi, reuseIdentifier: "Your Location")
//awalPin.pinTintColor = UIColor.blue

if let locationAwal = awalPlaceMark.location {
awalAnotasi.coordinate = locationAwal.coordinate
}

let akhirAnotasi = MKPointAnnotation()
akhirAnotasi.title = "National Hospital"

if let locationAkhir = akhirPlaceMark.location {
akhirAnotasi.coordinate = locationAkhir.coordinate
}

let awalPin = MyPointAnnotation()
awalPin.coordinate = awalAnotasi.coordinate
awalPin.pinTintColor = .green
awalPin.title = awalAnotasi.title

let akhirPin = MyPointAnnotation()
akhirPin.coordinate = akhirAnotasi.coordinate
akhirPin.pinTintColor = .blue
akhirPin.title = akhirAnotasi.title

//titik marker
self.mapRoute.showAnnotations([awalPin, akhirPin], animated: true)

//menambahkan route
let directionRequest = MKDirectionsRequest()
directionRequest.source = awalMap
directionRequest.destination = akhirMap
directionRequest.transportType = .automobile

let directions = MKDirections(request: directionRequest)
directions.calculate
{
(response, error) -> Void in

guard let response = response else
{
if let error = error {
print("Error : \(error)")
}
return
}

let route = response.routes[0]

self.mapRoute.add((route.polyline), level: MKOverlayLevel.aboveRoads)

let rect = route.polyline.boundingMapRect
self.mapRoute.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
self.mapRoute.delegate = self

}
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

extension LocationViewController : MKMapViewDelegate
{
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.lineWidth = 1.0
renderer.strokeColor = UIColor.red

return renderer
}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

var annotView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView
if annotView == nil {
annotView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
}
else {
annotView?.annotation = annotation
}
if let annotation = annotation as? MyPointAnnotation {
annotView?.pinTintColor = annotation.pinTintColor
annotView?.canShowCallout = true
}

return annotView
}

}

extension LocationViewController : CLLocationManagerDelegate
{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

if let location = locations.last
{
if((location.horizontalAccuracy) < CLLocationAccuracy(0))
{
return
}

lokasiAwal2 = location

self.updateUIAndGetDirection()
self.corLoc.stopUpdatingLocation() //avoiding continue direction changes
}
}

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

if status == CLAuthorizationStatus.authorizedWhenInUse || status == CLAuthorizationStatus.authorizedAlways {

manager.startUpdatingLocation()

}
}
}

Hope this helps

How to return the value of CLLocationManager didUpdateLocations in function?

class userCurrentLocation: NSObject, CLLocationManagerDelegate {

var locationManager:CLLocationManager!
var cityName:String!

//1. Add a closure callback
var didGetCity: ((String) -> Void)?

//2. Send City Name when received
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last!.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
self.cityName = placemark.locality ?? ""

// Check for null
// Send city name in the completion block
if (self.didGetCity != nil){
self.didGetCity?(self.cityName)
}
manager.stopUpdatingLocation()
}
}
}

//3. Add code to your class where you need the city Name

var City : String?

let locationManager = userCurrentLocation (cityName: "")
locationManager.didGetCity = {
[weak self] city in
self!.City = city
}

Does CLLocationManager ever use cellular data?

Location manager needs to consult remote server to resolve cell tower and wifi access point ids to lat/long. If wifi is not available, it has no option but to consume cellular data.

You can't force location manager to use GSM only: even if you set desiredAccuracy to 1 meter, location manager will try to deliver you the first reading as soon as possible and most likely it will be a result of wifi access point triangulation.

So the answer is yes, your app is going to use cellular data from time to time. However, keep in mind that under default settings iOS uses location services for it's own purposes - things like app suggestion, frequent locations feature etc. So it has to connect to Apple servers with or without your app running.

Furthermore, while I did not specifically test this, but I doubt that network traffic, which was created by location manager will be credited to your app under Cellular Data Usage section in Settings.

The bottom line - your app adds quite a few if any bytes towards the cellular data consumption and, most likely, even this amount will not be credited to your app.



Related Topics



Leave a reply



Submit