How do I configure my app to detect shake motion events? - ios

So let me clarify first off with showing what code i have implemented
-(BOOL)canBecomeFirstResponder
{
return YES;
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self becomeFirstResponder];
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
NSLog(#"FUUU");
}
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
NSLog(#"FUUU");
}
}
this is all in a UIViewController class
and in - (void) applicationDidFinishLaunching:(UIApplication*)application i have set
[application setApplicationSupportsShakeToEdit:YES];
Yet it does nothing, not nary single motion detected. I'm not sure what else to do. This seems to have worked for many other people, so i am baffled as to why i am different...Could it be because it's through a UINavigationController? or because i load the main application via a plist and my main looks like so
retVal = UIApplicationMain(argc, argv, nil, nil);
I am quite thoroughly stumped.

In order to detect shake gesture you need to subclass the UIView and implement the following methods (Do not implement these methods on UIViewController):
(BOOL)canBecomeFirstResponder
(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
Then add the subclassed view to UIViewController and make it first responder by calling -becomeFirstResponder on it.

You don't need to set [application setApplicationSupportsShakeToEdit:YES]; because this is the default value and iOS use this only for Shake to Undo/Redo.
If you want to capture the motion in your view controller you need to set this property to NO [application setApplicationSupportsShakeToEdit:NO]; and handle it by yourself.
As described in Event Handling Programming Guide.
Hope this helps.
PS: Just in case, you call the wrong super in your viewDidAppear method. This should be :
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}

Thank you all for your responses but i ended up using a different method i accessed the accelerometer in the app delegate and then send a nsnotifcation via the nsnotification center to the current ui navigation controller displayed
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil userInfo:nil];
}

Related

How to implement motion sensor in ios? [duplicate]

On the shake of the iPhone device i want some function to be called, i dont know how to recognize shake so i tried some link and tried this code
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(event.type == UIEventSubtypeMotionShake)
{
NSLog(#"called");
[self.view setBackgroundColor:[UIColor greenColor]];
}
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
But alas no luck so can you please let me know how i can do the same
Thanks and Regards
As per the above comments discussed i am answering my question so that others can read the solution that i have got
In the UIResponder class documentation it is said that the motion events will respond to the first responder only so what i did was add just a small function and that did the trick for me, so here's my solution
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(event.type == UIEventSubtypeMotionShake)
{
NSLog(#"called");
[self.view setBackgroundColor:[UIColor greenColor]];
}
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
Now still i was not able to detect any shake motion so all i had to do was to make my viewcontroller the first responder and for that here's the code that i used
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
and i was done
This was the solution that i came up with
Thanks and Regards
You could do something like this...
Firstly...
Set the applicationSupportsShakeToEdit property in the App's Delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.applicationSupportsShakeToEdit = YES;
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
Secondly...
Add/Override canBecomeFirstResponder, viewDidAppear: and viewWillDisappear: methods in your View Controller:
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
Thirdly...
Add the motionEnded method to your View Controller:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
// your code
}
}
That should work if the first answer did not and this is only quickly typed not tested:)
To globally handle shake across all screens of your app, you can subclass UIWindow and implement motionEnded, since the event will bubble up the responder chain until it reaches the window-object. Example using Xamarin:
public class MyWindow : UIWindow
{
public MyWindow(CoreGraphics.CGRect frame)
: base (frame)
{ }
public override void MotionEnded (UIEventSubtype motion, UIEvent evt)
{
if (motion == UIEventSubtype.MotionShake) {
Console.WriteLine ("Shake received");
}
}
}
Next, in AppDelegate, implement the window method to return an instance of your UIWindow subclass. Example using Xamarin:
public class AppDelegate : UIApplicationDelegate
{
UIWindow _window;
public override UIWindow Window
{
get
{
if (_window == null) {
_window = new MyWindow (UIScreen.MainScreen.Bounds);
}
return _window;
}
set
{
}
}
// ...
}
Voilà, shake is handled across the whole app.
Things become easier with SSEventListener. Your view controller doesn't need to override motionEnded:withEvent: any more! With SSEventListener, you can listener for shake event as simple as this:
[viewController ss_setShakeEventListener:^{ ... }];

Remote Control for audio doesn't work

after I managed to play audio in background with MPMoviePlayerController and tried to make it receive remote controls. But when I click on the Play/Pause button there's no reaction and the audio keeps on playing.
Then I tried to show if there's a log-output but there's no output.
Here's my Code:
-(void)viewDidAppear:(BOOL)animated{
...
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
NSLog(#"remoteControlReceivedWithEvent: %#", event);
if (event.type==UIEventTypeRemoteControl) {
if (event.subtype==UIEventSubtypeRemoteControlPlay) {
NSLog(#"Play");
} else if (event.subtype==UIEventSubtypeRemoteControlPause) {
NSLog(#"Pause");
} else if (event.subtype==UIEventSubtypeRemoteControlTogglePlayPause) {
NSLog(#"Play Pause");
}
}
}
Thanks for your effort in advanced.
It looks like your view controller isn't in the responder chain.
That might be due to [self resignFirstResponder] in viewWillDisappear and/or another ViewController is in the front.
To make sure you receive these events you can implement a UIApplication subclass which will be able to receive the events always. So it doesn't matter which ViewController is visible.
The UIApplication is always in the end of the responder chain.
Create a subclass for UIApplication (MyUIApplication) and implements the following UIResponder methods:
- (void)remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
...
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
In main.m use the following to initiate the app (replace MyUIApplication and AppDelegate with your classes names):
#autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([MyUIApplication class]), NSStringFromClass([AppDelegate class]));
}

TouchesEnded and TouchesCancelled not called

Okay I tested the following with the tabbed application template on Xcode 4.5/iOS 6.
Created a tabbed application.
Created a UIButton subclass called SampleButton and implemented
the following mothods:
- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches
withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
}
- (void) touchesMoved:(NSSet *)touches
withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches
withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
}
Added this SampleButton to the first tab.
Added breakpoints to all the touch methods.
Run on device.
Tested that all the touch methods are firing as expected.
Now touch the SampleButton and then also press the second tab.
RESULT: View switches to second tab but touchesCancelled and/or touchesEnded are never called in SampleButton. Shouldn't one or the other of those fire if the view changes while I'm touching that button? This is proving to be a huge issue because, in my app I'm playing a sound while that button is down and it never stops playing if the user switches tabs while pressing it. Seems like this used to work fine in iOS3 and iOS4.
It appears that when a view is removed from its window, it dissociates itself from any touches that were associated with it. So when the touch finally ends, the system doesn't send touchesEnded:… or touchesCancelled:… to the view.
Workaround by disabling tab switching
If you want to just disable tab switching while the button is pressed, you can do that by giving the tab bar controller a delegate and having the delegate return NO from tabBarController:shouldSelectViewController:. For example, in the your test app, you can have FirstViewController make itself the tab bar controller's delegate:
- (void)viewWillAppear:(BOOL)animated {
self.tabBarController.delegate = self;
}
And the view controller can allow the tab bar controller to select a tab only when the button is not pressed (highlighted):
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
return !_button.highlighted;
}
Workaround by detecting button highlight resetting to NO
When the button is removed from its window, it resets its highlighted property to NO. So one generic way to work around this problem is by using key-value observing (KVO) to monitor the button's state (instead of relying on the button to send you actions). Set yourself up as an observer of the button's highlighted property like this:
static int kObserveButtonHighlightContext;
- (void)viewDidLoad {
[super viewDidLoad];
[_button addObserver:self forKeyPath:#"highlighted"
options:NSKeyValueObservingOptionOld
context:&kObserveButtonHighlightContext];
}
- (void)dealloc {
[_button removeObserver:self forKeyPath:#"highlighted"
context:&kObserveButtonHighlightContext];
}
I discovered in testing that the button sends an extra KVO notification when it's removed from the window, before it resets its highlighted property back to NO. So when handling the KVO notification, check that the value has actually changed:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == &kObserveButtonHighlightContext) {
if ([change[NSKeyValueChangeOldKey] boolValue] != _button.highlighted) {
[self updatePlaybackForButtonState];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Finally, start or stop playback according to the highlighted property of the button:
- (void)updatePlaybackForButtonState {
if (_button.highlighted) {
NSLog(#"start playback");
} else {
NSLog(#"end playback");
}
}
Subclassing the button seems like the hard way to do this. Just tell the audio player to stop playing in the viewDidDisappear:animated: method, as R.A. suggested.
I worked around this by setting targets for the actions instead of using the touch methods:
[self addTarget:self action:#selector(handleKeyPressed) forControlEvents:UIControlEventTouchDown];
[self addTarget:self action:#selector(handleKeyReleased) forControlEvents:UIControlEventTouchUpInside];
[self addTarget:self action:#selector(handleKeyReleased) forControlEvents:UIControlEventTouchCancel];
These seems to get fired correctly even when the view swaps out.

IOS How to shake to change View

I've already know how to catch the shake gesture with the code below
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
// Put in code here to handle shake
NSLog(#"Device Shaked");
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
The NSLog shows that it did received the shake
but how to push another view,or back to the last view, as we know the code below can only use in ViewController class instead of View class, this Leavesview is handle by a LeavesviewController, and I use this viewController in another PDFViewController to display, so how could I make it jump to another view or dismiss back???
UIViewController *myView = [[UIViewController alloc]init];
[self.navigationController pushViewController:myView animated:YES];
See this complet Code......
- (BOOL) canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake)
{
// Work which you want
}
}
also dont forget these two methods...
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self resignFirstResponder];
}
Ok, Obviously I am just a starter 2 months ago. Also, obviously the answer is the Delegation;

How do I detect when someone shakes an iPhone?

I want to react when somebody shakes the iPhone. I don't particularly care how they shake it, just that it was waved vigorously about for a split second. Does anyone know how to detect this?
In 3.0, there's now an easier way - hook into the new motion events.
The main trick is that you need to have some UIView (not UIViewController) that you want as firstResponder to receive the shake event messages. Here's the code that you can use in any UIView to get shake events:
#implementation ShakingView
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if ( event.subtype == UIEventSubtypeMotionShake )
{
// Put in code here to handle shake
}
if ( [super respondsToSelector:#selector(motionEnded:withEvent:)] )
[super motionEnded:motion withEvent:event];
}
- (BOOL)canBecomeFirstResponder
{ return YES; }
#end
You can easily transform any UIView (even system views) into a view that can get the shake event simply by subclassing the view with only these methods (and then selecting this new type instead of the base type in IB, or using it when allocating a view).
In the view controller, you want to set this view to become first responder:
- (void) viewWillAppear:(BOOL)animated
{
[shakeView becomeFirstResponder];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[shakeView resignFirstResponder];
[super viewWillDisappear:animated];
}
Don't forget that if you have other views that become first responder from user actions (like a search bar or text entry field) you'll also need to restore the shaking view first responder status when the other view resigns!
This method works even if you set applicationSupportsShakeToEdit to NO.
From my Diceshaker application:
// Ensures the shake is strong enough on at least two axes before declaring it a shake.
// "Strong enough" means "greater than a client-supplied threshold" in G's.
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
#interface L0AppDelegate : NSObject <UIApplicationDelegate> {
BOOL histeresisExcited;
UIAcceleration* lastAcceleration;
}
#property(retain) UIAcceleration* lastAcceleration;
#end
#implementation L0AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[UIAccelerometer sharedAccelerometer].delegate = self;
}
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
histeresisExcited = YES;
/* SHAKE DETECTED. DO HERE WHAT YOU WANT. */
} else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
histeresisExcited = NO;
}
}
self.lastAcceleration = acceleration;
}
// and proper #synthesize and -dealloc boilerplate code
#end
The histeresis prevents the shake event from triggering multiple times until the user stops the shake.
I finally made it work using code examples from this Undo/Redo Manager Tutorial.
This is exactly what you need to do:
Set the applicationSupportsShakeToEdit property in the App's Delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.applicationSupportsShakeToEdit = YES;
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
Add/Override canBecomeFirstResponder, viewDidAppear: and viewWillDisappear: methods in your View Controller:
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
Add the motionEnded method to your View Controller:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
// your code
}
}
First, Kendall's July 10th answer is spot-on.
Now ... I wanted to do something similar (in iPhone OS 3.0+), only in my case I wanted it app-wide so I could alert various parts of the app when a shake occurred. Here's what I ended up doing.
First, I subclassed UIWindow. This is easy peasy. Create a new class file with an interface such as MotionWindow : UIWindow (feel free to pick your own, natch). Add a method like so:
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"DeviceShaken" object:self];
}
}
Change #"DeviceShaken" to the notification name of your choice. Save the file.
Now, if you use a MainWindow.xib (stock Xcode template stuff), go in there and change the class of your Window object from UIWindow to MotionWindow or whatever you called it. Save the xib. If you set up UIWindow programmatically, use your new Window class there instead.
Now your app is using the specialized UIWindow class. Wherever you want to be told about a shake, sign up for them notifications! Like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(deviceShaken) name:#"DeviceShaken" object:nil];
To remove yourself as an observer:
[[NSNotificationCenter defaultCenter] removeObserver:self];
I put mine in viewWillAppear: and viewWillDisappear: where View Controllers are concerned. Be sure your response to the shake event knows if it is "already in progress" or not. Otherwise, if the device is shaken twice in succession, you'll have a li'l traffic jam. This way you can ignore other notifications until you're truly done responding to the original notification.
Also: You may choose to cue off of motionBegan vs. motionEnded. It's up to you. In my case, the effect always needs to take place after the device is at rest (vs. when it starts shaking), so I use motionEnded. Try both and see which one makes more sense ... or detect/notify for both!
One more (curious?) observation here: Notice there's no sign of first responder management in this code. I've only tried this with Table View Controllers so far and everything seems to work quite nicely together! I can't vouch for other scenarios though.
Kendall, et. al - can anyone speak to why this might be so for UIWindow subclasses? Is it because the window is at the top of the food chain?
I came across this post looking for a "shaking" implementation. millenomi's answer worked well for me, although i was looking for something that required a bit more "shaking action" to trigger. I've replaced to Boolean value with an int shakeCount. I also reimplemented the L0AccelerationIsShaking() method in Objective-C. You can tweak the ammount of shaking required by tweaking the ammount added to shakeCount. I'm not sure i've found the optimal values yet, but it seems to be working well so far. Hope this helps someone:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7] && shakeCount >= 9) {
//Shaking here, DO stuff.
shakeCount = 0;
} else if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7]) {
shakeCount = shakeCount + 5;
}else if (![self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.2]) {
if (shakeCount > 0) {
shakeCount--;
}
}
}
self.lastAcceleration = acceleration;
}
- (BOOL) AccelerationIsShakingLast:(UIAcceleration *)last current:(UIAcceleration *)current threshold:(double)threshold {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
PS:
I've set the update interval to 1/15th of a second.
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
In iOS 8.3 (perhaps earlier) with Swift, it's as simple as overriding the motionBegan or motionEnded methods in your view controller:
class ViewController: UIViewController {
override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent) {
println("started shaking!")
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
println("ended shaking!")
}
}
You need to check the accelerometer via accelerometer:didAccelerate: method which is part of the UIAccelerometerDelegate protocol and check whether the values go over a threshold for the amount of movement needed for a shake.
There is decent sample code in the accelerometer:didAccelerate: method right at the bottom of AppController.m in the GLPaint example which is available on the iPhone developer site.
This is the basic delegate code you need:
#define kAccelerationThreshold 2.2
#pragma mark -
#pragma mark UIAccelerometerDelegate Methods
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
if (fabsf(acceleration.x) > kAccelerationThreshold || fabsf(acceleration.y) > kAccelerationThreshold || fabsf(acceleration.z) > kAccelerationThreshold)
[self myShakeMethodGoesHere];
}
Also set the in the appropriate code in the Interface. i.e:
#interface MyViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate>
Check out the GLPaint example.
http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html
Add Following methods in ViewController.m file, its working properly
-(BOOL) canBecomeFirstResponder
{
/* Here, We want our view (not viewcontroller) as first responder
to receive shake event message */
return YES;
}
-(void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if(event.subtype==UIEventSubtypeMotionShake)
{
// Code at shake event
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"Motion" message:#"Phone Vibrate"delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
[self.view setBackgroundColor:[UIColor redColor]];
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self becomeFirstResponder]; // View as first responder
}
Sorry to post this as an answer rather than a comment but as you can see I'm new to Stack Overflow and so I'm not yet reputable enough to post comments!
Anyway I second #cire about making sure to set the first responder status once the view is part of the view hierarchy. So setting first responder status in your view controllers viewDidLoad method won't work for example. And if you're unsure as to whether it is working [view becomeFirstResponder] returns you a boolean that you can test.
Another point: you can use a view controller to capture the shake event if you don't want to create a UIView subclass unnecessarily. I know it's not that much hassle but still the option is there. Just move the code snippets that Kendall put into the UIView subclass into your controller and send the becomeFirstResponder and resignFirstResponder messages to self instead of the UIView subclass.
First off, I know this is an old post, but it is still relevant, and I found that the two highest voted answers did not detect the shake as early as possible. This is how to do it:
Link CoreMotion to your project in the target's build phases:
In your ViewController:
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Shake detected.
}
}
Easiest solution is to derive a new root window for your application:
#implementation OMGWindow : UIWindow
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
// via notification or something
}
}
#end
Then in your application delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//…
}
If you are using a Storyboard, this may be trickier, I don’t know the code you will need in the application delegate precisely.
Just use these three methods to do it
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
for details you may check a complete example code over there
A swiftease version based on the very first answer!
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if ( event?.subtype == .motionShake )
{
print("stop shaking me!")
}
}
In swift 5, this is how you can capture motion and check
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if motion == .motionShake
{
print("shaking")
}
}
To enable this app-wide, I created a category on UIWindow:
#implementation UIWindow (Utils)
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Do whatever you want here...
}
}
#end

Resources