iOS Device Motion: Apps With Attitude

Stephan Baker
Forest Giant
Published in
5 min readFeb 2, 2015

--

Today I had the opportunity to work on an application that needed to be aware of changes to the device orientation. Detecting whether the phone is being held in a landscape or portrait orientation is easy enough, and iOS can handle making these changes for you, but I needed something a bit more specialized. The view I was creating would always be displayed in portrait orientation, but I needed to be able to capture the angle at which the user was holding the phone.

If you’re guessing that the Core Motion Framework was the answer, you’re right. This framework is able to provide us with information that could be used to determine how the user may be holding the device. If you’re interested in doing this in one of your projects, I will describe the process of attaining these values below.

Getting Started

It turned out to be pretty easy to get the values we needed. First, we need to declare an instance of the CMMotionManager class, which Apple describes as the gateway to the motion services provided by iOS. It is good practice to share this instance throughout your application to maintain the highest level of performance.

let motionManager = CMMotionManager()

Now that we have initialized our manager, Apple has provided a number of properties to determine whether the service we are interested in is available. In our case, we are interested in the device-motion service, so we should check the value of deviceMotionAvailable before continuing.

if motionManager.deviceMotionAvailable { 
//do something interesting
}

Now that we’re sure the services are available to us, we can begin retrieving the desired data in one of two ways.

Synchronous Method

The first method is to pull the data synchronously. To do this, we first call the startDeviceMotionUpdates function. Once the service is started, we can simply grab our manager’s deviceMotion property to get the desired data.

motionManager.startDeviceMotionUpdates()
var data = motionManager.deviceMotion

This is simple enough, but our application needs to be able to update the data in real time. Rather than try to write something to manage this on our own, Apple has provided an alternate method for accessing the data asynchronously.

Asynchronous Method

To access the device-motion data asynchronously, we first need to call startDeviceMotionUpdatesToQueue:withHandler. This function expects an NSOperationQueue instance to regulate the execution of our operations as well as a block which conforms to the CMDeviceMotionHandler type. For the purposes of this example, we will pass NSOperationQueue’s currentQueue property for our first parameter. Since these updates might happen at a high rate, it is important to note that Apple does not recommend using the main operation queue. Don’t worry about the call to handleDeviceMotionUpdate right now; we will touch on that in a little bit.

motionManager.startDeviceMotionUpdatesToQueue(
NSOperationQueue.currentQueue(), withHandler: {
(deviceMotion, error) -> Void in

if(error == nil) {
self.handleDeviceMotionUpdate(deviceMotion)
} else {
//handle the error
}
})

Once we make the call above, we will begin receiving callbacks at a set interval, allowing us to handle the data in a way that suits our application. If we need to adjust this time interval, we can simply set the deviceMotionUpdateInterval property of our device-motion manager.

motionManager.deviceMotionUpdateInterval = 0.1

Stopping Updates

At some point in the future of our application’s execution, it may make sense us to stop processing device-motion updates. We can do this easily by calling the stopDeviceMotionUpdates function.

motionManager.stopDeviceMotionUpdates()

Attitude: Pitch, Roll, and Yaw

Now that we are receiving device-motion updates, we can get the desired angles of rotation from the CMDeviceMotion object returned by our update handler. This class has a property called attitude, which describes the rotation of our device in terms of roll, pitch, and yaw.

If we are holding our phone in portrait orientation, the roll describes the angle of rotation about the axis that runs through the top and bottom of the phone. The pitch describes the angle of rotation about the axis that runs through the sides of your phone (where the volume buttons are). And finally, the yaw describes the angle of rotation about the axis that runs through the front and back of your phone. With these three values, we can determine how the user is holding their phone in reference to what would be level ground. These values are provided in radians, so be sure to convert them to degrees if that is your desired unit of measurement.

func degrees(radians:Double) -> Double {
return 180 / M_PI * radians
}

Knowing this, we can implement the handleDeviceMotionUpdate function you saw earlier.

func handleDeviceMotionUpdate(deviceMotion:CMDeviceMotion) {
var attitude = deviceMotion.attitude
var roll = degrees(attitude.roll)
var pitch = degrees(attitude.pitch)
var yaw = degrees(attitude.yaw)
println(“Roll: \(roll), Pitch: \(pitch), Yaw: \(yaw)”)
}

One Last Problem

If we look at the values we are printing to the console above, you may notice something strange about the data. When holding the phone flat, with the screen facing up towards the sky, the value of our pitch is roughly zero degrees. As we tilt the phone so that the screen is facing us, we see the value of pitch will rise, approaching 90 degrees. If we continue tilting beyond this point, we will then see the value decline, approaching zero degrees once again.

For the application we were creating, we needed to be able to distinguish between these values. There are a couple of ways to solve this problem, but for the sake of simplicity, we reference the gravity property of CMDeviceMotion to determine whether the phone is facing up or down. Using this in conjunction with the values we are receiving for pitch, we can easily identify the values we want to accept.

Summary

Though it comes with some caveats, Apple made it pretty easy for us to determine the device rotation using the Core Motion Framework. By utilizing the device-motion service, we can get the attitude of our device, described as three values known as roll, pitch, and yaw. There are many ways we may be able to use this data when creating solutions for our applications in the future. However, it is important to keep in mind that not all of these solutions will result in a good user experience. Twisting and turning your device can be awkward, not to mention dangerous to the health of your device (I’ve dropped mine three or four times today). Be sure and think of some ways that you could use Core Motion to enhance your applications, but proceed with caution.

Originally published on Forest Giant’s blog.

--

--

Stephan Baker
Forest Giant

I'm a computer engineer who focuses on creativity. Designing and developing games is what I love to do, and I have experience on both PC and mobile platforms.