- Accessing Basic Device Information
- Adding Device Capability Restrictions
- Recipe: Checking Device Proximity and Battery States
- Recipe: Recovering Additional Device Information
- Recipe: Using Acceleration to Locate "Up"
- Working with Basic Orientation
- Retrieving the Current Accelerometer Angle Synchronously
- Recipe: Using Acceleration to Move Onscreen Objects
- Recipe: Accelerometer-Based Scroll View
- Recipe: Core Motion Basics
- Recipe: Retrieving and Using Device Attitude
- Detecting Shakes Using Motion Events
- Recipe: Using External Screens
- Tracking Users
- One More Thing: Checking for Available Disk Space
- Summary
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) [motionManager startAccelerometerUpdatesToQueue: [[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] initWithFrame:(CGRect){.size=size}]; [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]; } @end