iOS : How to detect Shake motion?
Asked Answered
N

5

25

I added the following code to my appDelegate.m

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake )
    {
        // User was shaking the device. Post a notification named "shake".
        [[NSNotificationCenter defaultCenter] postNotificationName:@"shake" object:self];
    }
}

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{   
}

- (void)shakeSuccess
{
    // do something...
}

and then I added :

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    // INIT THE BACKGROUND PATH STRING

    [self refreshFields];
    [self.window addSubview:navController.view];
    [self.window makeKeyAndVisible];
    ***[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shakeSuccess) name:@"shake" object:nil];***

    return YES;
}

When I start my app on my iPhone, the method named "shakeSuccess" doesn't called. What should I do to implement this function in my app? any idea?

Novelette answered 14/4, 2012 at 15:51 Comment(0)
C
59

This might help you...
https://mcmap.net/q/93051/-how-do-i-detect-when-someone-shakes-an-iphone

He says that you should set the UIApplication's applicationSupportsShakeToEdit to YES. and override 3 methods in your VC:

-(BOOL)canBecomeFirstResponder {
    return YES;
}

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated {
    [self resignFirstResponder];
    [super viewWillDisappear:animated];
}

The rest of your code is fine. (the -motionEnded:withEvent:)

Card answered 14/4, 2012 at 15:55 Comment(2)
Lol and he never did it.Avra
Why call [self resignFirstResponder]; ahead of [super viewWillDisappear:animated];? This seems peculiar.Peppergrass
P
26

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:)

Put answered 1/4, 2013 at 22:46 Comment(0)
C
14

If you want to be able to detect the shake motion across the app, the best way is to override the class of your application with a custom class.

And then implement this in your custom application class

@implementation PSApplication

- (void)sendEvent:(UIEvent *)event
{
    if ( event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake ) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"shakeNotification" object:nil];
    }

    [super sendEvent:event];
}

@end
Colquitt answered 21/5, 2014 at 9:44 Comment(5)
This is the best answer for those looking for global in-app shake detecting code, not bound to single view controller.Lifeless
In order to override the UIApplication in your main.m put this: return UIApplicationMain(argc, argv, @"PSApplication", NSStringFromClass([AppDelegate class]));Isagogics
Why not subclass - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event directly?Zulazulch
you can do that too i guess... :-) There's more than one way to skin a cat. But what if your UIApplication is not the first responder, would it work?Colquitt
There will be two send events per one shake. Tested in simulator.Kwarteng
H
3

I extended UIApplication class and added class reference to main: MyApplication.h

@interface MyApplication : UIApplication

@end

MyApplication.m

@implementation MyApplication

- (void) sendEvent:(UIEvent *)event
{
    if( event && (event.subtype==UIEventSubtypeMotionShake))
    {
        AppDelegate *objAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

        [objAppDelegate doWhatEver];
        [super sendEvent:event];
    }
    else
    {
        [super sendEvent:event];
    }
}

@end

And the last step in main.m

int main(int argc, char *argv[])
{
return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class]), NSStringFromClass([AppDelegate class]));
}

This works in all cases.

Huntsman answered 5/3, 2015 at 9:15 Comment(1)
Nice solution! CheersKnapp
R
0

Swift detect global Shake motion

create custom UIWindow class

import UIKit

class CustomWindow : UIWindow {
    override var canBecomeFirstResponder: Bool {
        return true
    }
    
    override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        guard motion == .motionShake else {
            return
        }
        
        //logic is here
    }
}

setup CustomWindow

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: CustomWindow!

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }

        //inti view controller as you wish
        //or if your storyboard has `Is Initial View Controller`
        let storyboard = UIStoryboard(name: String(describing: SomeViewController.self), bundle: nil)
        let vc = storyboard.instantiateInitialViewController()

        window = CustomWindow(windowScene: windowScene)
        window.rootViewController = vc
        window.makeKeyAndVisible()
    }
}
Reportorial answered 20/8, 2023 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.