Search code examples
iosswiftuiinterfaceorientationcmmotionmanager

Correct CMMotionManager yaw for current UIInterfaceOrientation


I am using the CMMotionManger to get yaw readings with the following code.

Regardless of the UIInterfaceOrientation, I am trying to get readings minus 1.5708 rad if yawed 90 degrees right, and positive 1.5708 rad if the device is yawed 90 degrees left (not concerned with polarity as it can be reversed as needed).

I am able to get it to do what I want when the device is in portrait orientation. In radians, it gives me around -1.5708 when the device is yawed 90 degrees to the right and around 1.5708 rads when spun to the left.

But when the device is in portrait upside down orientation, when the yaw is spun to the right, it starts around -2.4 down to around -3.14 then jumps to ~3.14 down to 2.6. How can I make it smooth and continuous 0 to -1.5708 rad?

I also need to correct for landscape left and right.

if motionManager == nil {
    motionManager = CMMotionManager()
}

let updateInterval: NSTimeInterval = 1 / 24.0 //24hz

if (motionManager!.accelerometerAvailable) {
    motionManager!.accelerometerUpdateInterval = updateInterval

    motionManager!.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: { (motion:CMDeviceMotion?, error: NSError?) -> Void in

        print("\(motion!.attitude.yaw)")

        switch (TDTDeviceUtilites.interfaceOrientation()) {
            case UIInterfaceOrientation.Portrait:
                // No correction needed
                break;

            case UIInterfaceOrientation.PortraitUpsideDown:
                //need to apply correction
                break;

            case UIInterfaceOrientation.LandscapeRight:
                //need to apply correction
                break;

            case UIInterfaceOrientation.LandscapeLeft:
                //need to apply correction
                break;
        }
    })
}

Solution

  • It turns out that CMMotionManager orients itself for the current UIInterfaceOrientation. So the easiest solution is to stop and restart the CMMotionManager upon device rotation. (Wish I would have known this it would have saved me much frustration!) For instance:

    public override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
         stopMotionUpdates()
         motionManager = nil
         startMotionUpdates()
    }
    
    func stopMotionUpdates() {              
        motionManager?.stopMagnetometerUpdates()
        motionManager?.stopDeviceMotionUpdates()
        motionManager?.stopAccelerometerUpdates()
    }
    
    func startMotionUpdates() { 
        //Start motion updates is the code in the question above
    }