Home > Articles > Mobile Application Development & Programming

This chapter is from the book

Recipe: Core Motion Basics

The Core Motion framework centralizes motion data processing. Introduced in the iOS 4 SDK, Core Motion supersedes the direct accelerometer access you’ve just read about. It provides centralized monitoring of three key onboard sensors. These sensors are composed of the gyroscope, which measures device rotation; the magnetometer, which provides a way to measure compass bearings; and the accelerometer, which detects gravitational changes along three axes. A fourth entry point called device motion combines all three of these sensors into a single monitoring system.

Core Motion uses raw values from these sensors to create readable measurements, primarily in the form of force vectors. Measurable items include the following properties:

  • Device attitude (attitude)—The device’s orientation relative to some frame of reference. The attitude is represented as a triplet of roll, pitch, and yaw angles, each measured in radians.
  • Rotation rate (rotationRate)—The rate at which the device rotates around each of its three axes. The rotation includes x, y, and z angular velocity values measured in radians per second.
  • Gravity (gravity)—The device’s current acceleration vector as imparted by the normal gravitational field. Gravity is measured in g’s, along the x, y, and z axes. Each unit represents the standard gravitational force imparted by Earth (namely 32 feet per second per second, or 9.8 meters per second per second).
  • User acceleration (userAcceleration)—The acceleration vector being imparted by the user. Like gravity, user acceleration is measured in g’s along the x, y, and z axes. When added together, the user vector and the gravity vector represent the total acceleration imparted to the device.
  • Magnetic field (magneticField)—The vector representing the overall magnetic field values in the device’s vicinity. The field is measured in microteslas along the x, y, and z axes. A calibration accuracy is also provided, to inform your application of the field measurements quality.

Testing for Sensors

As you read earlier in this chapter, you can use the application’s Info.plist file to require or exclude onboard sensors. You can also test an in-app for each kind of possible Core Motion support:

if (motionManager.gyroAvailable)
    [motionManager startGyroUpdates];

if (motionManager.magnetometerAvailable)
    [motionManager startMagnetometerUpdates];

if (motionManager.accelerometerAvailable)
    [motionManager startAccelerometerUpdates];

if (motionManager.deviceMotionAvailable)
    [motionManager startDeviceMotionUpdates];

Starting updates does not produce a delegate callback mechanism like you encountered with the UIAccelerometer class. Instead, you are responsible for polling each value, or you can use a block-based update mechanism that executes a block that you provide at each update (for example, startAccelerometerUpdatesToQueue:withHandler:).

Handler Blocks

Recipe 1-6 adapts Recipe 1-4 for use with Core Motion. The acceleration callback has been moved into a handler block, and the x and y values are read from the data’s acceleration property. Otherwise, the code remains unchanged. Here, you see the Core Motion basics: A new motion manager is created. It tests for accelerometer availability. It then starts updates using a new operation queue, which persists for the duration of the application run.

The establishMotionManager and shutDownMotionManager methods enable your application to start up and shut down the motion manager on demand. These methods are called from the application delegate when the application becomes active and when it suspends:

- (void) applicationWillResignActive:(UIApplication *)application
    [tbvc shutDownMotionManager];

- (void) applicationDidBecomeActive:(UIApplication *)application
    [tbvc establishMotionManager];

These methods provide a clean way to shut down and resume motion services in response to the current application state.

Recipe 1-6. Basic Core Motion

@implementation TestBedViewController
- (void) tick
    butterfly.transform = CGAffineTransformIdentity;

    // Move the butterfly according to the current velocity vector
    CGRect rect = CGRectOffset(butterfly.frame, xvelocity, 0.0f);
    if (CGRectContainsRect(self.view.bounds, rect))
        butterfly.frame = rect;

    rect = CGRectOffset(butterfly.frame, 0.0f, yvelocity);
    if (CGRectContainsRect(self.view.bounds, rect))
        butterfly.frame = rect;

    butterfly.transform =
       CGAffineTransformMakeRotation(mostRecentAngle + M_PI_2);

- (void) shutDownMotionManager
    NSLog(@"Shutting down motion manager");
    [motionManager stopAccelerometerUpdates];
    motionManager = nil;

    [timer invalidate];
    timer = nil;

- (void) establishMotionManager
    if (motionManager)
        [self shutDownMotionManager];

    NSLog(@"Establishing motion manager");

    // Establish the motion manager
    motionManager = [[CMMotionManager alloc] init];
    if (motionManager.accelerometerAvailable)
             [[NSOperationQueue alloc] init]
         withHandler:^(CMAccelerometerData *data, NSError *error)
             // Extract the acceleration components
             float xx = -data.acceleration.x;
             float yy = data.acceleration.y;
             mostRecentAngle = atan2(yy, xx);

             // Has the direction changed?
             float accelDirX = SIGN(xvelocity) * -1.0f;
             float newDirX = SIGN(xx);
             float accelDirY = SIGN(yvelocity) * -1.0f;
             float newDirY = SIGN(yy);

             // Accelerate. To increase viscosity,
             // lower the additive value
             if (accelDirX == newDirX)
                 xaccel = (abs(xaccel) + 0.85f) * SIGN(xaccel);
             if (accelDirY == newDirY)
                 yaccel = (abs(yaccel) + 0.85f) * SIGN(yaccel);

             // Apply acceleration changes to the current velocity
             xvelocity = -xaccel * xx;
             yvelocity = -yaccel * yy;

    // Start the physics timer
    timer = [NSTimer scheduledTimerWithTimeInterval: 0.03f
        target: self selector: @selector(tick)
        userInfo: nil repeats: YES];

- (void) initButterfly
    CGSize size;

    // Load the animation cells
    NSMutableArray *butterflies = [NSMutableArray array];
    for (int i = 1; i <= 17; i++)
        NSString *fileName =
            [NSString stringWithFormat:@"bf_%d.png", i];
        UIImage *image = [UIImage imageNamed:fileName];
        size = image.size;
        [butterflies addObject:image];

    // Begin the animation
    butterfly = [[UIImageView alloc]
    [butterfly setAnimationImages:butterflies];
    butterfly.animationDuration = 0.75f;
    [butterfly startAnimating];

    // Set the butterfly's initial speed and acceleration
    xaccel = 2.0f;
    yaccel = 2.0f;
    xvelocity = 0.0f;
    yvelocity = 0.0f;

    // Add the butterfly
    butterfly.center = RECTCENTER(self.view.bounds);
    [self.view addSubview:butterfly];

- (void) loadView
    [super loadView];
    self.view.backgroundColor = [UIColor whiteColor];
    [self initButterfly];
  • + Share This
  • 🔖 Save To Your Account