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
Installed App from Testflight Crashes Due to Alleged Uisearchdisplaycontroller
Skspritenode Position in Universal Game
App Crashes After Executing Background Fetch Completionhandler
Unknown Class in Interface Builder
Presenting Uialertcontroller from Uitableviewcell
Exception with Insertobject:Atindex: on iOS6
iOS 8+ Framework with Nested Embedded Framework
How to Add Watermark on a Exist Image
Is -[Uitableview Reloaddata] Asynchronous or Synchronous
Xcode 11.4. Navigation's Title Color Gone Black from Storyboard
Loading a Welcome Screen (Splash Screen) Before Tabbarcontroller
How to Efficiently Find Cgrects for Visible Words in Uitextview
What's Currently the "Correct" Way to Set a Uiview's Corner Radius
"Unable to Validate Your Application Error" While Uploading a New Version of iOS App
Static Linking with Swift, Xcode6-Beta
Move Gmsmarker on Google Map Like Uber
How to Play a Video from Either a Local or a Server Url in iOS