- 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
Detecting Shakes Using Motion Events
When the iPhone detects a motion event, it passes that event to the current first responder, the primary object in the responder chain. Responders are objects that can handle events. All views and windows are responders and so is the application object.
The responder chain provides a hierarchy of objects, all of which can respond to events. When an object toward the start of the chain receives an event, that event does not get passed further down. The object handles it. If it cannot, that event can move on to the next responder.
Objects often become the first responder by declaring themselves to be so, via becomeFirstResponder. In this snippet, a UIViewController ensures that it becomes the first responder whenever its view appears onscreen. Upon disappearing, it resigns the first responder position:
- (BOOL)canBecomeFirstResponder { return YES; } // Become first responder whenever the view appears - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self becomeFirstResponder]; } // Resign first responder whenever the view disappears - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self resignFirstResponder]; }
First responders receive all touch and motion events. The motion callbacks mirror UIView touch callback stages. The callback methods are as follows:
- motionBegan:withEvent:—This callback indicates the start of a motion event. At the time of writing this book, there was only one kind of motion event recognized: a shake. This may not hold true for the future, so you might want to check the motion type in your code.
- motionEnded:withEvent:—The first responder receives this callback at the end of the motion event.
- motionCancelled:withEvent:—As with touches, motions can be canceled by incoming phone calls and other system events. Apple recommends that you implement all three motion event callbacks (and, similarly, all four touch event callbacks) in production code.
The following snippet shows a pair of motion callback examples. If you test this on a device, you can notice several things. First, the began and ended events happen almost simultaneously from a user perspective. Playing sounds for both types is overkill. Second, there is a bias toward side-to-side shake detection. The iPhone is better at detecting side-to-side shakes than the front-to-back and up-down versions. Finally, Apple’s motion implementation uses a slight lockout approach. You cannot generate a new motion event until a second or so after the previous one was processed. This is the same lockout used by Shake to Shuffle and Shake to Undo events:
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { // Play a sound whenever a shake motion starts if (motion != UIEventSubtypeMotionShake) return; [self playSound:startSound]; } - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { // Play a sound whenever a shake motion ends if (motion != UIEventSubtypeMotionShake) return; [self playSound:endSound]; }