I have a simple question. I'm trying to detect when a user shakes the iPhone. I have the standard code in place to detect the motion and this works no problem. However, in testing this on my actual phone, I've realized that you have to shake the device quite hard to get the motion detection to trigger. I would like to know if there is a way to implement a level of sensitivity checking. For example, a way to detect if a user lightly shakes the device or somewhere between light and hard shake. This will be targeted towards iOS 7 so any tips or advice that is not deprecated from older iOS version would be greatly appreciated. I've done my googling but have yet to find any good solutions to this problem (If there are any.)
Thanks!
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(motion == UIEventSubtypeMotionShake)
{
//Detected motion, do something about it
//at this point.
}
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
-(void)viewWillDisappear:(BOOL)animated
{
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
This is a swift version based on zic10's answer, with the addition of a flag that prevents getting a few extra calls to your motion handler even when the first line in that handler is
motionManager.stopDeviceMotionUpdates()
.Also, a value of around
3.0
can be useful if you want to ignore the shake, but detect a bump. I found0.3
to be way too low as it ended up being more like "detect move". In my tests, the ranges were more like:Here is the complete view controller for an Xcode single VC template:
And the storyboard I used for this test looks like this:
It's also worth noting that CoreMotion is not testable in the simulator. Because of this constraint you may still find it worthwhile to additionally implement the UIDevice method of detecting motion shake. This would allow you to manually test shake in the simulator or give UITests access to shake for testing or tools like fastlane's snapshot. Something like:
And then use Ctrl-Cmd-Z to test shake in the simulator.
Heres how I did this using Swift 3.
Import CoreMotion and create an instance
On ViewDidLoad or wherever you want to start checking for updates:
This function takes the rotation rate and gets a sum for the absolute values for x,y and z movements
Here is the solution I found. This works well but you do have to play with the deviceMotionUpdateInterval time value as well as the accelerationThreshold which can be tricky to get a fine balancing act for a actual "light shake" vs "picking up the phone and moving it closer to your face etc..." There might be better ways but here is one to start. Inside of my view didLoad I did something like this:
Use core motion. Link your binary with CoreMotion framework. Include #import in your class. Create an instance of CMMotionManager. Set the deviceMotionUpdateInterval property to a suitable value. Then call startDeviceMotionUpdatesToQueue. You will get continuous updates inside the block, which includes acceleration, magnetic field, rotation, etc. You will get the data you require. One thing to be taken care of is that the update shall be so rapid if the interval is too small, and hence you will have to employ suitable logic to handle the same.