We would like to rotate a SCNNode
so as to match with the North direction.
Arkit allows to align its z-axis with the North but this requires user permission for location tracking, and we don't want to ask for it. see gravityandheading
As a consequence, we're trying to use the magnetic field property of CMDeviceMotion. But we have no idea how to do that. There are probably some matrix calculations, but we don't master them for the time being. see magneticfield
Any help would be highly appreciated! thanks!
Here are some tips on how to setup it properly for AR apps and NON-AR apps:
In iOS 11 and higher here's what you need to do to get location working:
info.plist
and request authorisation from the location manager asking it to start. There are two Property List Keys
in info.plist for the location authorisation. One or both of these keys is required. In NON-AR apps if neither of the keys are there, you can call startUpdatingLocation
method but the location manager won’t actually start. It won’t send a failure message to the delegate either (since it never started, it can’t fail). It will also fail if you add one or both of the keys but forget to explicitly request authorisation.Use these two
Property List Keys
ininfo.plist
file. They are used since iOS 11 was released:
<key>NSLocationWhenInUseUsageDescription</key>
<string>App needs an access to your location (in foreground)</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>App needs an access to your location (in background)</string>
And these two
Property List Keys
were deprecated earlier (DON'T USE THESE KEYS):
<key>NSLocationUsageDescription</key>
<string>App needs an access to your location (in background)</string>
<key>NSLocationAlwaysUsageDescription</key>
<true/>
Here's how your code for AR apps should look like:
let configuration = ARWorldTrackingConfiguration()
func session(_ session: ARSession, didFailWithError error: Error) {
switch error.code {
case 101:
configuration.worldAlignment = .gravity
restartSession()
default:
configuration.worldAlignment = .gravityAndHeading
restartSession()
}
}
func restartSession() {
self.sceneView.session.pause()
self.sceneView.session.run(configuration,
options: [.resetTracking, .removeExistingAnchors])
}
For NON-AR apps use these two instance methods: requestAlwaysAuthorization()
(requests permission to use location services whenever the app is running) and requestWhenInUseAuthorization()
(requests permission to use location services while the app is in the foreground).
Here's how your code for NON-AR apps should look like:
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
func updateMyLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
if locationManager.respondsToSelector(#selector(CLLocationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.startUpdatingLocation()
}
}
}
Also, you can request Always Authorization
:
let locationManager = CLLocationManager()
func enableLocationServices() {
locationManager.delegate = self
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
break
case .restricted, .denied:
// Disable location features
disableMyLocationBasedFeatures()
break
case .authorizedWhenInUse:
// Enable basic location features
enableMyWhenInUseFeatures()
break
case .authorizedAlways:
// Enable any of your app's location features
enableMyAlwaysFeatures()
break
}
}
Hope this helps.