The iPhone OS uses the accelerometer to handle autorotation, and many games use it as a control mechanism.. iPhone, the accelerometer will detect a greater amount of force on one or more
Trang 1Whee!
ne of the coolest features of the iPhone and iPod Touch is the built-in acceler-
ometer, the tiny device that lets the iPhone know how it’s being held and if it’s
being moved The iPhone OS uses the accelerometer to handle autorotation,
and many games use it as a control mechanism It can also be used to detect
shakes and other sudden movement
Accelerometer Physics
An accelerometer measures both acceleration and gravity by sensing the
amount of inertial force in a given direction The accelerometer inside iPhone
is a three-axis accelerometer, meaning that it is capable of detecting either
movement or the pull of gravity in three-dimensional space As a result, you
can use the accelerometer to tell not only how the phone is currently being
held (as autorotation does) but also if it’s laying on a table and even whether it’s face down or face up
“Uw
Accelerometers give measurements in g-forces (“g” for gravity), so a value of 1.0 returned by the accelerometer means that 1 g is sensed in a particular
direction If the iPhone is being held still with no movement, there will be
approximately 1 g of force exerted on it by the pull of the earth If the iPhone
is being held upright, in portrait orientation, the iPhone will detect and report about 1 g of force exerted on its y axis If the iPhone is being held at an angle,
that 1 g of force will be distributed along different axes depending on how the iPhone is being held When held at a 45-degree angle, that 1 g of force will be split roughly equally between two of the axes
Sudden movement can be detected by looking for accelerometer values con-
siderably larger than 1 g In normal usage, the accelerometer does not detect
significantly more than 1 g on any axis If you shake, drop, or throw your
Trang 2iPhone, the accelerometer will detect a greater amount of force on one or more axes Please
do not drop or throw your own iPhone just to test this theory
You can see a graphic representation of the three axes used by iPhone's accelerometer in
Figure 15-1 One thing to notice is that the accelerometer uses the more standard conven-
tion for the y coordinate, with increases in y indicating upward force, which is the opposite
of Quartz 2D’s coordinate system When you are using the accelerometer as a control mecha- nism with Quartz 2D, you have to translate the y coordinate When working with OpenGL ES, which you are more likely to be using if you are using the accelerometer to control anima- tion, no translation is required
Figure 15-1 The ¡Phone accelerometer S axes in three dimensions
Accessing the Accelerometer
The UIAccelerometer class exists as a singleton To retrieve a reference to the class, call the method sharedAccelerometer, like so:
Trang 3Getting information from the accelerometer is similar to getting information from Core Location You create a class that conforms to the UIAccelerometerDel egate protocol, implement a method to which the accelerometer will provide information, and specify an instance of that class to be the accelerometer’s delegate
When you assign a delegate, you need to specify an update interval in seconds iPhone’s accelerometer supports polling at a rate of up to 100 times per second, although there is
no guarantee that it will actually update you that many times or that those updates will be exactly evenly spaced To assign a delegate and specify a polling interval of 60 times per second, you would do this:
accelerometer.delegate = self;
Once you've done that, all that’s left is to implement the method that the accelerometer uses
to update its delegate, accelerometer: didAccelerate: This method takes two arguments The first is a reference to the shared UIAccelerometer instance The second contains the actual data from the accelerometer, embedded in an object of the class UIAcceleration Before we look at the delegate method, let’s talk about the UIAccel eration object that’s used to pass the information to the delegate
UlAcceleration
We mentioned earlier that the iPhone's accelerometer detects acceleration along three axes, and it provides this information to the delegate using instances of the UIAcceleration class Each UIAcceleration instance has an x, y, and z property, each of which holds a signed float-
ing point value A value of 0 means that the accelerometer detects no movement on that
particular axis A positive or negative value indicates force in one direction For example, a neg- ative value for y indicates that downward pull is sensed, which is probably an indication that the phone is being held upright in portrait orientation A positive value for y indicates some force is being exerted in the opposite direction, which could mean the phone is being held upside down or that the phone is being moved in a downward direction
Keeping the diagram in Figure 15-1 in mind, let's look at some accelerometer results Note
that, in real life, you will almost never get values this precise, as the accelerometer is sensi- tive enough to pick up even tiny amounts of movement, and you will usually pick up at least
some tiny amount of force on all three axes This is real-world physics and not high school
physics lab
Trang 4
X:0.0 y:-1.0 Z:0.0
x;1.0 y:0.0 z:0.0
X30.0 ÿ‡1‹0 Z?70:0 » ;=1.,0 y:0.0 z:0.0
S——— x:0.0 y:0.0 z:-1.0
a x:¿ooy:o.oz:i.o
Figure 15-2 Idealized acceleration values for different device orientations
Implementing the accelerometer:didAccelerate: Method
In order to receive accelerometer information, the class you specify as the accelerometer’s delegate needs to implement the accelerometer: didAccelerate: method If you wanted
to display the acceleration values in a UILabel, you would implement that method like this:
Trang 5- (void)accelerometer: (UIAccelerometer *)accelerometer
didAccelerate: (UIAcceleration *)acceleration {
NSString *newText = [[NSString alloc]
initWwithFormat:@"Max: x: %g\ty:%g\tz:%g", acceleration.x,
To check for a shake, check for an absolute value greater than 1.5 for a slight shake and 2.0 for a strong shake, like this:
- (void)accelerometer: (UIAccelerometer *)accelerometer
didAccelerate: (UIAcceleration *)acceleration {
- (void)accelerometer: (UIAccelerometer *)accelerometer
didAccelerate: (UIAcceleration *)acceleration {
Trang 6static NSInteger shakeCount = 0;
static NSDate *shakeStart;
NSDate *now = [[NSDate alloc] init];
NSDate *checkDate = [[NSDate alloc] initWithTimeInterval:1.5f
sinceDate: shakeStart] ;
if C[now compare: checkDate] ==
NSOrderedDescending || shakeStart == nil) {
shakeStart = [[NSDate alloc] init];
This method keeps track of the number of times the accelerometer reports a value above 2, and if it happens four times within a second and a half span of time, it registers as a shake
Accelerometer as Directional Controller
Probably the most common usage of the accelerometer in third-party applications is as
a controller for games Instead of using buttons to control the movement of a character or object in a game, the accelerometer is used In a car racing game, for example, twisting the
iPhone like a steering wheel might steer your car, while tipping it forward might accelerate
and tipping back might brake
Exactly how you use the accelerometer as a controller is going to vary greatly depending
on the specific mechanics of the game In the simplest cases, you might just take the value from one of the axes, multiply it by a number, and tack that on to the coordinates of the controlled objects In more complex games where physics are modeled more realistically, you would have to make adjustments to the velocity of the controlled object based on the values returned from the accelerometer
Trang 7The one tricky aspect of using the accelerometer as a controller is that the delegate method
is not guaranteed to call back at the interval you specify If you tell the accelerometer to update your delegate class 60 times a second, all that you can say for sure is that it won't update you more than 60 times a second You're not guaranteed to get 60 evenly spaced updates every second, so if you're doing animation based on input from the accelerometer, you have to keep track of the time that passes between delegate method calls
We'll create a program that uses the accelerometer for input a little later in the chapter, but
first, we're going to break your phone
Shake and Break
OK, we're not really going to break your phone, but we're going to write an application that
detects shakes and then makes your phone look and sound like it broke as a result of the shake When you launch the application, the program will display a picture that looks like the iPhone home page (see Figure 15-3)
Shake the phone hard
enough, though, and your
poor phone will make
a sound that you never
want to hear coming out
of a consumer electronics
device What's more, your
screen will look like the one
shown in Figure 15-4 Why
do we do these evil things?
Not to worry You can reset
the iPhone to its previously
pristine state by touching
the screen Figure 15-3 The ShakeAnd- Figure 15-4 but handle it
innocuous enough
Trang 8The Code That Breaks
Create a new project in Xcode using the view-based application template Call the new proj- ect ShakeAndBreak In the 75 ShakeAnaBreak folder of the project archive, we've provided you the two images and the sound file you need for this application, so drag home.png, homebroken.png, and glass.wav to the Resources folder of your project There’s also an icon
png in that folder Add that to the Resources folder as well
Next, expand the Resources folder, and single-click info.plist We need to add an entry to the property list to tell our application not to use a status bar, so single-click the row that says Information Property List, and click the button that appears at the end of the row to add a new
child Change the new row’s Key to U/StatusBarHidden Now, control-click (or right-click if you
have a two-button mouse) the empty Value column in the row you just added A contextual menu should appear (see Figure 15-5) From that menu, select the Value Type submenu, and then select Boolean The row should change to have a checkbox Click the checkbox so that it
is checked Finally, type icon.png in the Value column next to the /con file key
Localization native development re en Copy
Bundle display name - ${PROE Paste
Executable file ${EXEC
Icon file Shift Row Right
InfoDictionary version 6.0
Bundle creator OS Type code 777? Add Row
Bundle version 1.0 Show Raw Keys/Values
LSRequiresIPhoneOS ww Data
Reveal in Finder Number
Reveal in Group Tree v String
Add to Bookmarks Get Info
Figure 15-5 Changing the Value Type for UIStatusBarHidden
Now, expand the Classes folder We’re going to need to create an outlet to point to an image view so that we can change the displayed image We'll also need a couple of UIImage instances to hold the two pictures, a sound ID to refer to the sound, and a Boolean to keep track of whether the screen needs to be reset Single-click ShakeAndBreakViewController.h, and add the following code:
Trang 9@property Cnonatomic, retain) UIImageView *imageView;
@property Cnonatomic, retain) UIImage *fixed;
@property Cnonatomic, retain) UIImage *broken;
@end
In addition to the instance variables and properties, notice that we’ve conformed the class
to the UIAccelerometerDelegate protocol and defined two constants, one for the update frequency and the other to define how many g-forces the accelerometer has to detect before
it qualifies as a shake We've defined the update frequency at a fairly low frequency of ten
updates a second, which is sufficient for detecting a shake Generally, you want to poll at the
lowest frequency that will meet your needs When using the accelerometer as a controller, you'll need to poll at a considerably faster rate, usually between 30 and 60 updates per second Save the header file, and double-click ShakeAndBreakViewController.xib to open the file in Interface Builder Single-click the View icon, and press 883 to bring up the size inspector Change the view's height from 460 to 480 so that it takes up the additional screen space made available by getting rid of the status bar Drag an Image View over from the library
to the window labeled View The image view should automatically resize to take up the full window, so just place it so that it sits perfectly within the window
Control-drag from the File’s Owner icon to the image view, and select the imageView outlet Now save and close the nib file, and go back to Xcode When you get there, single-click the ShakeAndBreakController.m file, and make the following changes:
NSString *path = [L[LNSBundle mainBundle] pathForResource:@"glass"
ofType: @"wav"];
Trang 10AudioServicesCreateSystemSoundID(C(CFURLRef) [NSURL
fileURLWithPath: path], &soundID);
self.fixed = [UIImage imageNamed:@"home.png"] ;
self.broken = [UIImage imageNamed:@"homebroken.png"];
imageView.image = fixed;
brokenScreenShowing = NO;
(BOOL) shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return CinterfaceOrientation == UIInterfaceOrientationPortrait);
(void) didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data (void)dealloc {
- (void) accelerometer: (UIAccelerometer *)accelerometer
didAccelerate: (UIAcceleration *)acceleration {
Trang 11The first method we implement is vi ewDidLoad, where we get a reference to the shared accelerometer instance, set self to be the accelerometer’s delegate, and then set the update frequency using the constant we defined earlier:
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = kUpdatelInterval;
Load the Simulation Files
Next, we load the glass sound file into memory and save the assigned identifier in the soundID instance variable
NSString *path = [L[NSBundle mainBundle] pathForResource:@"glass"
ofType:@"wav”];
fileURLWithPath:path], &soundID);
We then load the two images into memory:
self.fixed = [UIImage imageNamed:@"home.png"] ;
self.broken = [UIImage imageNamed:@"homebroken.png"] ;
Finally, we set imageVi ew to show the unbroken screenshot and set brokenScreenShowing
to NO to indicate that the screen does not currently need to be reset:
imageView.image = fixed;
brokenScreenShowing = NO;
The next new method is the accelerometer delegate method In it, we check
brokenScreenShowing If it is NO, we know the screen is already showing the broken
image, so we don’t want to do anything
if C! brokenScreenShowing) {
Otherwise, we check all three of the axes passed in and see if any of them exceed the accel- eration threshold we defined earlier If any of the three axes do, we set the image view to
show the broken image, play the sound, and set brokenScreenShowing to YES so that we
don't do this again until the user has reset the screen:
if Cacceleration.x > kAccelerationThreshold || acceleration.y > kAccelerationThreshold || acceleration.z >
kAccelerationThreshold) {
imageView.image = broken;
AudioServicesPlaySystemSound CsoundTD);
brokenScreenShowing = YES;