Magnetic Heading sample code for iOS - ios

Can anyone provide me with a short snippet that will return me the magnetic heading of the iPhone?
I do not want Objective-C please. I need it in Swift.
I have written these lines so far but it does not return me any value:
let locManager = CLLocationManager()
locManager.desiredAccuracy = kCLLocationAccuracyBest
locManager.requestWhenInUseAuthorization()
locManager.startUpdatingLocation()
locManager.startUpdatingHeading()
locManager.headingOrientation = .portrait
locManager.headingFilter = kCLHeadingFilterNone
print(locManager.heading?.trueHeading.binade as Any)
Thanks!

You didn't set the delegate for the location manager. iOS does not update your location right away. Rather, it will call a function provided by your delegate when it has a location / heading update. The reason behind this setup is efficiency. Instead of 10 apps having 10 different location managers competing for time on the GPS hardware, these 10 location managers will request to be notified when the GPS has an update.
Try this:
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var label: UILabel!
var locManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locManager.desiredAccuracy = kCLLocationAccuracyBest
locManager.requestWhenInUseAuthorization()
locManager.headingOrientation = .portrait
locManager.headingFilter = kCLHeadingFilterNone
locManager.delegate = self // you forgot to set the delegate
locManager.startUpdatingLocation()
locManager.startUpdatingHeading()
}
// MARK: -
// MARK: CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Manager failed: \(error)")
}
// Heading readings tend to be widely inaccurate until the system has calibrated itself
// Return true here allows iOS to show a calibration view when iOS wants to improve itself
func locationManagerShouldDisplayHeadingCalibration(_ manager: CLLocationManager) -> Bool {
return true
}
// This function will be called whenever your heading is updated. Since you asked for best
// accuracy, this function will be called a lot of times. Better make it very efficient
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
label.text = "\(newHeading.magneticHeading)"
}
}

Related

How to add current timestamp in camera in ios app

Something like this I want to add current timestamp when recording is done in the video and save into camera roll.
**Note **: I have successfully added current location and timestamp in AVVideoPreviewLayer but it is static after exporting video is show static time does not show running timestamp
Try creating a layer of previewLayer: UIView with the components you want to have on camera.
Add this layer to currently running session of AVCaptureVideoPreviewLayer.
This link might help you to achieve your problem
Adding objects over camera view in app (Swift 3) not under camera view?
I tried something out. I guess you want to get latitude, longitude and the timestamp.
You find an example for latitude and longitude below.
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.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func showLocation(_ sender: Any) {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if(CLLocationManager.locationServicesEnabled()){
locationManager.startUpdatingLocation()
}
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
}
To get the current time you can use something like this:
let date = NSDate()
print(date)
The result for both in console should look something like this:
2017-12-17 21:45:39 +0000
user latitude = 37.3322499
user longitude = -122.056264
(The time looks like this in my example because im from EU but you can convert it to what you want)
You can add this to a separate view and bring it to front with
view.bringSubview(toFront: YourView)
I hope this is helpful!

CoreLocation Delegate functions do not run until view controller viewDidLoad function is finished

This is more than likely something very easy to solve but I've been at it for some time and can't seem to get at an answer.
I would like to know why a Delegate method for CLLocationManager does not trigger until after the ViewDidLoad function when the CLLocationManager is loaded within the ViewDidLoad function.
I have set my default region to Sydney Australia within my App Scheme and I have encapsulated my locationManager within its own class as follows:
import UIKit
import CoreLocation
/* Class location is a class to track user location and return a location object. */
class usrLocation: NSObject, CLLocationManagerDelegate
{
//MARK: Properties
var locationMgr: CLLocationManager!
var location: CLLocation!
var seenError: Bool = false
//MARK: Public Methods
func startTracking() {
locationMgr = CLLocationManager()
locationMgr.delegate = self
locationMgr.desiredAccuracy = kCLLocationAccuracyBest
locationMgr.requestWhenInUseAuthorization()
locationMgr.startUpdatingLocation()
}
//Return a location object
func getLocation() -> CLLocation {
locationMgr.startUpdatingLocation()
return location!
}
//MARK: CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationMgr.stopUpdatingLocation()
if (seenError == false) {
seenError = true
print(error)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
location = (locations ).last
locationMgr.stopUpdatingLocation()
}
}
I have initialised the class in my ViewController and try to begin tracking my current location in viewDidLoad.
The code looks like this.
override func viewDidLoad() {
var location = usrLocation()
override func viewDidLoad() {
super.viewDidLoad()
// Track location
location.startTracking()
location.getLocation()
//update Label text
sLongitude.text = "\(location.getLocation().coordinate.longitude)"
sLatitude.text = "\(location.getLocation().coordinate.latitude)"
}
getLocation() never returns location as it is always nil as the delegate didUpdateLocations function does not run. Why is this?
I tried to get this working using a completion handler but couldn't so my solution has been to ignore setting the label text via viewDidLoad and to instead update the labels once the location variable has been set. That way viewDidLoad completes, the delegate gets called and the labels get updated.
var location: CLLocation! {
didSet {
//update Label text
sLongitude.text = "\(location.getLocation().coordinate.longitude)"
sLatitude.text = "\(location.getLocation().coordinate.latitude)"
}
}

location manager not updating in swift ios10

I am new to swift and am trying to get a basic program that display the longitude and latitutde.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var latLabel: UILabel!
#IBOutlet weak var longLabel: UILabel!
#IBOutlet weak var addLabel: UILabel!
var lm = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
lm = CLLocationManager()
}
#IBAction func getCurrentLocation(_ sender: AnyObject) {
lm.delegate = self
lm.desiredAccuracy = kCLLocationAccuracyBest
lm.startUpdatingLocation()
print("loc")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print ("error")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print ("location")
let length = locations.count
let curLoc = locations[length-0]
latLabel.text = String(curLoc.coordinate.latitude)
print ("loccation")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You are missing two things.
you have to ask for permission using requestAlwaysAuthorization or requestWhenInUseAuthorization().
So when you are allocating your location manager in viewdidlod, Do like this
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
You need to add information as #Anbu.Karthik has suggested in his answer.
Use NSLocationAlwaysUsageDescription for apps that want to use the
device's location even when the app is not open and being used.
Use NSLocationWhenInUseUsageDescription for apps that want to use the
device's location only when the app is open and in use.
check once are you added the following information your plist
Location :
Key : Privacy - Location Always Usage Description
Value : $(PRODUCT_NAME) location use
Key : Privacy - Location When In Use Usage Description
Value : $(PRODUCT_NAME) location use
for more information see this
One other issue that I found caused the location update callback not to be called, with no obvious error message given is that it must not be a private function (which is obvious once you finally notice it, of course...). I realise the OP's one is not but this is a common place to end up if you are having issues, so it may help someone.
So the following will cause the function not to be called without giving any obvious error indication like crashing the app etc:
private func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print ("location")
let length = locations.count
let curLoc = locations[length-0]
latLabel.text = String(curLoc.coordinate.latitude)
print ("loccation")
}
Removing the 'private' keyword, as in the OP's code, will allow it to be called.

Value of distance between 2 CLLocation points does not appear on label text

I am trying to calculate the distance from the starting position I am (when i first start the app) to the current position and present it on a label (meters.text) but nothing appears on it..
Here is my code:
import UIKit
import CoreLocation
class CorrectViewController: UIViewController,CLLocationManagerDelegate {
#IBOutlet weak var meters: UILabel!
var wr: Int = 0
var distance = CLLocationDistance()
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization() //starts when i use the app
self.locationManager.distanceFilter = kCLHeadingFilterNone
self.locationManager.startUpdatingLocation()
}
//locatioManager delegate method
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
let currentLocation: CLLocation = newLocation
let startLocation: CLLocation = oldLocation
distance = startLocation.distanceFromLocation(currentLocation)
let tripString = NSString(format: "%.2f", distance)
meters.text = ("\(tripString)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
meters.text = ("error!")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Any help is appreciated:)
It appears that the location update delegate method is inside the viewDidLoad function and hence defines a local function rather than implementing the delegate method.
Move the following method out of the viewDidLoad scope:
locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation)
In addition you are not requesting permission to use the users location. Make sure to do that and add the necessary string to your info plist.
Also: oldLocation will always be the previous location (not your initial one).
Alright, after a lot of searching and "trial and error" I found the solution here.
Instead of using the method didUpdateToLocation the method didUpdateLocations should be used. Moreover, it is really important to put NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys in the .plist file (value may be an additional message that will be presented in location alert). These keys are required in iOS 8.

Swift - User Location Issues

I've just started with swift and I'm having an issue. I've read the various threads about user location and map kits and can't solve my issue. I had the code running and could create regions as I wanted and I could zoom into the user location.
I've paired the code back to try and locate the issue and the code left is below. The issue is that the userlocation is coming back as a nil value when you try and run the simulator which crashes the app. What am I doing wrong as I've completed authorising user location so surely it shouldn't be coming back nil. At one point I had code to zoom on the user location AFTER initially setting a region elsewhere and calling a function to do the zoom, but if you initially try and call the user location its always nil so you can't initialise the map zooming into where the user is which is what I want.
import UIKit
import MapKit
class MapController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
// MARK: - location manager to authorize user location for Maps app
var locationManager = CLLocationManager()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
mapView.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
}
override func viewDidLoad() {
super.viewDidLoad()
checkLocationAuthorizationStatus()
var userLocation = locationManager.location
println("\(userLocation.coordinate.latitude)")
println("\(userLocation.coordinate.longitude)")
// Do any additional setup after loading the view.
}
}
Firstly, CLLocationManager updates user location asynchronously. That means that even after you call startUpdatingLocation() your location will be nil until location manager returns with the new location.
Secondly, in your code you are not actually calling this method. If you DO need to be able to store the user location then you should change your code to:
import UIKit
import MapKit
class MapController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
// MARK: - location manager to authorize user location for Maps app
lazy var locationManager: CLLocationManager = {
var manager = CLLocationManager()
manager.delegate = self
return manager
}()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
mapView.showsUserLocation = true
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
override func viewDidLoad() {
super.viewDidLoad()
checkLocationAuthorizationStatus()
//location is nil at this point because location update is
//an asynchronous operation!
//var userLocation = locationManager.location
//println("\(userLocation.coordinate.latitude)")
//println("\(userLocation.coordinate.longitude)")
// Do any additional setup after loading the view.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
//this is the place where you get the new location
println("\(location.coordinate.latitude)")
println("\(location.coordinate.longitude)")
}
}
There is only one minor thing to note. In the last function I am using an argument locations: [CLLocation]. This is definitely correct in Swift 2.0, but in Swift 1.2 it might be locations: [AnyObject] in which case you have to do a conditional downcast yourself.
Let me know if this works for you

Resources