A CMMotionManager object can give you readings from the accelerometer and gyroscope. But you won’t want to get the speed of rotation, either biased or unbiased. (Were you planning to put your iPhone on a phonograph turntable?) The purpose of the gyroscope is to give us the device’s attitude, as we mentioned here.

List of Core motion classes.

Source code in

  1. main.m
  2. Class AttitudeAppDelegate
  3. Class View
  4. Property list Attitude-Info.plist

Create the project

Project → Edit Active Target Attitude" → General → Linked Libraries
Add CoreMotion.framework.

Add the Required Device Capabilities key to Attitude-Inf.plist, with the values accelerometer and gyroscope.

Rotation matrix, Euler angles (yaw/pitch/roll), quaternions

The three Euler angles give the current attitude of the device in the form of three rotations from a starting attitude called the reference frame. The quaternion gives the current attitude of the device in the form of a single rotation of θ degrees from the reference frame. Which would you rather use? The range of the function acos is 0 to &pi, so the θ displayed by the app will range from 0° to 360°. Wikipedia article on rotations.

Things to try

  1. Display the numbers in a monospace font.
    	view.font = [UIFont fontWithName: @"Courier" size: 12];

  2. Push vs. pull. The draw method of class View checks the device’s attitude whenever it feels like it. This is called pulling the attitude from the motion manager.

    The other approach is to call the startGyroUpdatesToQueue:withHandler: of class CMMotionManager instead of using an NSTimer. This will make the motion manager push out the attitude to us, allowing us to receive every single attitude update. We have to write a block of code (with a caret) which will be executed every time an update arrives. The following variable handler is like a pointer to a function in C++. (An Objective-C selector does not know what class its method belongs to.) See Core Motion.

    	manager.deviceMotionUpdateInterval = 1.0 / hertz;
    	CMDeviceMotionHandler handler = ^ (CMDeviceMotion *motion, NSError *error) {
    		[self draw: motion]; //add an argument to the draw method of class View
    	//Shouldn't use the main queue, but probably okay for only 10 hertz.
    	[manager startDeviceMotionUpdatesToQueue:  [NSOperationQueue mainQueue]
    		withHandler: handler];

  3. By default, our attitude is measured with respect to the attitude in which the device was when we called the startDeviceMotionUpdates method of class CMMotionManager. We can change the reference frame with the multiplyByInverseOfAttitude: method of class CMAttitude:.
    	//a is the attitude with respect to the original attitude.
    	CMAttitude *a = manager.deviceMotion.attitude;
    	CMAttitude *other = //whatever;
    	[a multiplyByInverseOfAttitude: other];
    	//a is not the attitude with respect to the other attitude.