Как определить, когда кто-то трясет iPhone?

Я хочу реагировать, когда кто-то трясет iPhone. Меня не особо заботит, как они его встряхивают, только то, что его энергично махали в течение доли секунды. Кто-нибудь знает, как это обнаружить?


person Josh Gagnon    schedule 29.09.2008    source источник


Ответы (17)


В версии 3.0 теперь есть более простой способ - подключиться к новым событиям движения.

Основная хитрость заключается в том, что вам нужно иметь некоторый UIView (а не UIViewController), который вы хотите в качестве firstResponder для получения сообщений о событиях встряхивания. Вот код, который вы можете использовать в любом UIView для получения событий встряхивания:

@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

Вы можете легко преобразовать любой UIView (даже системные представления) в представление, которое может получить событие встряхивания, просто создав подкласс представления только с этими методами (и затем выбрав этот новый тип вместо базового типа в IB или используя его при выделении Посмотреть).

В контроллере представления вы хотите, чтобы это представление стало первым респондентом:

- (void) viewWillAppear:(BOOL)animated
{
    [shakeView becomeFirstResponder];
    [super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
    [shakeView resignFirstResponder];
    [super viewWillDisappear:animated];
}

Не забывайте, что если у вас есть другие представления, которые становятся первыми респондентами в результате действий пользователя (например, строка поиска или поле ввода текста), вам также необходимо восстановить статус первого респондента дрожащего представления, когда другое представление уходит в отставку!

Этот метод работает, даже если для параметра applicationSupportsShakeToEdit установлено значение NO.

person Kendall Helmstetter Gelner    schedule 10.07.2009
comment
У меня это не совсем сработало: мне нужно было переопределить viewDidAppear моего контроллера вместо viewWillAppear. Я не знаю почему; может быть, представление должно быть видимым, прежде чем оно сможет делать то, что делает, чтобы начать получать события встряхивания? - person Kristopher Johnson; 09.08.2009
comment
Я заметил, что в 3.1 что-то показалось забавным, возможно, это было так - возможно, представление не принимает статус первого респондента до этого момента (или это ошибка). Я исследую и запишу радар или обновлю свой код. - person Kendall Helmstetter Gelner; 09.08.2009
comment
Странно, я снова протестировал (под 3.0), и код работает - хотя в 3.1 в симуляторе он перестал работать, как у меня (хотя все еще работает на устройстве). - person Kendall Helmstetter Gelner; 13.08.2009
comment
Подождите секунду, вы используете canBecomeFirstResponder. :) Я попробую еще раз, только я могу переместить его в UIWindow (если это вообще целесообразно с точки зрения производительности?). - person Joe D'Andrea; 29.08.2009
comment
Отличный ответ, очень полезно собрать образец C # / MonoTouch вместе conceptdev .blogspot.com / 2009/10 / - person Conceptdev; 03.10.2009
comment
Поскольку viewController не является представлением, я бы подумал, что он не может быть частью цепочки респондентов. Мне нужно выяснить, почему это работает ... - person Kendall Helmstetter Gelner; 28.12.2009
comment
Вот странный случай: когда я запускаю жест встряхивания в симуляторе, метод motionEnded: withEvent: вызывается дважды с помощью UIEventSubtypeMotionShake. Есть идеи, почему это будет называться более одного раза? - person Cory Imdieke; 14.01.2010
comment
Это проще, но не обязательно лучше. Если вы хотите обнаружить длительное непрерывное сотрясение, этот подход бесполезен, поскольку iPhone любит срабатывать motionEnded до того, как встряхивание действительно прекратится. Таким образом, используя этот подход, вы получаете разрозненную серию коротких встряхиваний вместо одной длинной. Другой ответ в этом случае работает намного лучше. - person aroth; 13.04.2011
comment
@Kendall - UIViewControllers реализуют UIResponder и находятся в цепочке респондентов. Самый верхний UIWindow тоже. developer.apple.com/library/ios/DOCUMENTATION/EventHandling/ - person DougW; 23.06.2011
comment
[super respondsToSelector: не будет делать то, что вы хотите, поскольку это эквивалентно вызову [self respondsToSelector:, который вернет YES. Вам нужно [[ShakingView superclass] instancesRespondToSelector:. - person Vadim Yelagin; 21.02.2014
comment
Это действительно хороший момент, код всегда будет вызываться, поскольку оператор if всегда будет приравниваться к true. - person Kendall Helmstetter Gelner; 23.02.2014
comment
Вместо использования: if (event.subtype == UIEventSubtypeMotionShake) вы можете использовать: if (motion == UIEventSubtypeMotionShake) - person Hashim Akhtar; 28.03.2014

Из моего приложения Diceshaker:

// 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

Гистерезис предотвращает многократное срабатывание события встряхивания, пока пользователь не прекратит встряхивание.

person millenomi    schedule 01.10.2008
comment
Обратите внимание, что я добавил ответ ниже, представляющий более простой метод для версии 3.0. - person Kendall Helmstetter Gelner; 11.07.2009
comment
Что произойдет, если они встряхнут его именно на определенной оси? - person jprete; 14.08.2009
comment
Лучший ответ, потому что события motionBegan и motionEnded iOS 3.0 не очень точны или точны с точки зрения определения точного начала и конца встряски. Такой подход позволяет вам быть максимально точным. - person aroth; 13.04.2011
comment
UIAccelerometerDelegate акселерометр: didAccelerate: устарел в iOS5. - person Yantao Xie; 20.01.2012
comment
Этот другой ответ лучше и быстрее реализовать stackoverflow.com/a/2405692/396133 - person Abramodj; 21.04.2012
comment
@millenomi, как добиться энергичного встряхивания с помощью приведенного выше кода .. Я действительно борюсь здесь - person vinothp; 03.09.2012
comment
@millenomi, как я могу засечь движение устройства как панораму? - person user2526811; 17.09.2013
comment
Как определить дрожание, если iphone перемещается только в направлении X? Я не хочу обнаруживать дрожание в направлении Y или Z. - person Manthan; 24.05.2017

Я наконец заставил его работать, используя примеры кода из этого Учебное пособие по управлению отменой / повтором выполнения.
Это именно то, что вам нужно сделать:

  • Установите свойство applicationSupportsShakeToEdit в делегате приложения:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • Добавить / переопределить canBecomeFirstResponder, viewDidAppear: и viewWillDisappear: методы в вашем контроллере представления:
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    

  • Добавьте метод motionEnded в контроллер представления:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    
    person Eran Talmor    schedule 08.03.2010
    comment
    Просто чтобы добавить к решению Эрана Талмора: вы должны использовать контроллер навигации вместо контроллера просмотра в случае, если вы выбрали такое приложение в новом средстве выбора проекта. - person Rob; 16.08.2010
    comment
    Я пробовал использовать это, но иногда сильное или легкое встряхивание просто прекращалось - person vinothp; 10.08.2012
    comment
    Шаг 1 теперь избыточен, так как значение по умолчанию applicationSupportsShakeToEdit YES. - person uɥƃnɐʌuop; 22.04.2016
    comment
    Зачем звонить [self resignFirstResponder]; раньше [super viewWillDisappear:animated];? Это кажется странным. - person JaredH; 17.11.2016

    Во-первых, ответ Кендалла от 10 июля точен.

    Теперь ... Я хотел сделать что-то подобное (в iPhone OS 3.0+), только в моем случае я хотел, чтобы это было для всего приложения, чтобы я мог предупреждать различные части приложения при возникновении встряски. Вот что я в итоге сделал.

    Сначала я создал подкласс UIWindow. Это очень просто. Создайте новый файл класса с таким интерфейсом, как MotionWindow : UIWindow (не стесняйтесь выбирать свой, natch). Добавьте такой метод:

    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
        if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
        }
    }
    

    Измените @"DeviceShaken" на название уведомления по вашему выбору. Сохраните файл.

    Теперь, если вы используете MainWindow.xib (стандартный материал для шаблонов Xcode), зайдите в него и измените класс вашего объекта Window с UIWindow на MotionWindow или как вы его называете. . Сохраните xib. Если вы настроили UIWindow программно, используйте вместо этого свой новый класс Window.

    Теперь ваше приложение использует специализированный класс UIWindow. Где бы вы ни хотели, чтобы вам рассказали о встряхивании, подпишитесь на их уведомления! Нравится:

    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
    

    Чтобы удалить себя в качестве наблюдателя:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    Я поместил свой в viewWillAppear: и viewWillDisappear:, когда речь идет о контроллерах представления. Убедитесь, что ваш ответ на событие встряски знает, «уже происходит» оно или нет. В противном случае, если устройство встряхнуть дважды подряд, вы получите небольшую пробку. Таким образом, вы можете игнорировать другие уведомления, пока действительно не закончите отвечать на исходное уведомление.

    Также: вы можете выбрать отключение motionBegan или motionEnded. Тебе решать. В моем случае эффект всегда должен иметь место после, когда устройство находится в состоянии покоя (а не когда оно начинает трястись), поэтому я использую motionEnded. Попробуйте оба и посмотрите, какой из них имеет больше смысла ... или обнаружите / уведомите для обоих!

    Еще одно (любопытное?) Наблюдение: обратите внимание, что в этом коде нет никаких признаков управления первым респондентом. Пока что я пробовал это только с контроллерами табличного представления, и, похоже, все отлично работает вместе! Однако я не могу поручиться за другие сценарии.

    Кендалл и др. al - кто-нибудь может объяснить, почему это может быть так для подклассов UIWindow? Это потому, что окно находится на вершине пищевой цепи?

    person Joe D'Andrea    schedule 29.08.2009
    comment
    Вот что так странно. Работает как есть. Надеюсь, это не тот случай, когда он работает вопреки самой себе! Пожалуйста, дайте мне знать, что вы узнали в ходе собственного тестирования. - person Joe D'Andrea; 02.09.2009
    comment
    Я предпочитаю создавать подкласс обычного UIView, тем более что вам нужно, чтобы он существовал только в одном месте, поэтому включение подкласса окна кажется естественным. - person Shaggy Frog; 17.10.2009
    comment
    Спасибо jdandrea, я использую ваш метод, но трясусь несколько раз !!! я просто хочу, чтобы пользователи могли встряхнуть один раз (с учетом того, что там встряхивание) ...! как я могу справиться с этим? Я реализую что-то вроде этого. Я реализую свой код smht следующим образом: i-phone.ir/forums/uploadedpictures/ ---- но проблема в том, что приложение качается только 1 раз за все время жизни приложения! не только просмотр - person Momi; 10.08.2010
    comment
    Momeks: Если вам нужно, чтобы уведомление появлялось, когда устройство находится в состоянии покоя (после встряхивания), используйте motionEnded вместо motionBegan. Это должно сработать! - person Joe D'Andrea; 24.08.2010
    comment
    Это, безусловно, правильный способ сделать это. См. Пример кода Apple CLPaint. - person Thomas; 02.09.2010
    comment
    @Joe D'Andrea - он работает как есть, потому что UIWindow находится в цепочке респондентов. Если что-то выше по цепочке перехватит и поглотит эти события, оно не их получит. developer.apple.com/library/ios/DOCUMENTATION/EventHandling/ - person DougW; 23.06.2011
    comment
    Мне очень нравится этот ответ как простой способ избавиться от встряхивания в любом месте без суеты. - person Kendall Helmstetter Gelner; 11.02.2012
    comment
    Это здорово, но я бы, наверное, позвонил super, не так ли? - person beebcon; 22.06.2017

    Я наткнулся на этот пост в поисках "потрясающей" реализации. Ответ millenomi хорошо сработал для меня, хотя я искал что-то, что требовало немного большего «встряхивания» для срабатывания. Я заменил логическое значение на int shakeCount. Я также переопределил метод L0AccelerationIsShaking () в Objective-C. Вы можете настроить количество встряхивания, необходимое для настройки количества, добавляемого в shakeCount. Я еще не уверен, что нашел оптимальные значения, но, похоже, пока он работает хорошо. Надеюсь, это поможет кому-то:

    - (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: Я установил интервал обновления 1/15 секунды.

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
    
    person Community    schedule 10.11.2008
    comment
    Как я могу обнаружить движение устройства как панораму? - person user2526811; 17.09.2013
    comment
    Как определить дрожание, если iphone перемещается только в направлении X? Я не хочу обнаруживать дрожание в направлении Y или Z. - person Manthan; 24.05.2017

    В iOS 8.3 (возможно, ранее) с Swift это так же просто, как переопределение методов motionBegan или motionEnded в вашем контроллере представления:

    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!")
        }
    }
    
    person nhgrif    schedule 20.04.2015
    comment
    Вы пробовали? Я предполагаю, что для того, чтобы заставить это работать, когда приложение работает в фоновом режиме, потребуется немного дополнительных усилий. - person nhgrif; 11.05.2018
    comment
    Нет .. скоро .. но если вы заметили iPhone 6s, у него есть функция «поднять, чтобы разбудить» экран, когда экран на некоторое время светлеет, когда вы берете устройство в руки. Мне нужно зафиксировать это поведение - person nr5; 11.05.2018
    comment
    Это должен быть принятый ответ. Не нужно возиться с CoreMotion… - person John Scalo; 30.10.2020

    Вам нужно проверить акселерометр с помощью метода accelerometer: didAccelerate:, который является частью протокола UIAccelerometerDelegate, и проверить, превышают ли значения пороговое значение количества движения, необходимого для встряхивания.

    В акселерометре есть достойный образец кода: didAccelerate: метод прямо в нижней части AppController.m в примере GLPaint, который доступен на сайте разработчика iPhone.

    person Dave Verwer    schedule 29.09.2008

    Это базовый код делегата, который вам нужен:

    #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];   
        }
    

    Также установите соответствующий код в интерфейсе. то есть:

    @interface MyViewController: UIViewController ‹UIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate›

    person Benjamin Ortuzar    schedule 22.03.2009
    comment
    Как я могу обнаружить движение устройства, как панораму? - person user2526811; 17.09.2013
    comment
    Как определить дрожание, если iphone перемещается только в направлении X? Я не хочу обнаруживать дрожание в направлении Y или Z. - person Manthan; 24.05.2017

    Посмотрите пример GLPaint.

    http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html

    person camflan    schedule 29.09.2008

    Добавьте следующие методы в файл ViewController.m, он работает правильно

        -(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 
         }
    
    person Himanshu Mahajan    schedule 15.10.2012

    Извините, что опубликовал это как ответ, а не комментарий, но, как вы можете видеть, я новичок в Stack Overflow, и поэтому я еще недостаточно авторитетен, чтобы публиковать комментарии!

    В любом случае, я второй @cire об установке статуса первого респондента, когда представление является частью иерархии представлений. Таким образом, установка статуса первого респондента в ваших контроллерах представления viewDidLoad метод, например, не будет работать. И если вы не уверены, работает ли он, [view becomeFirstResponder] возвращает вам логическое значение, которое вы можете проверить.

    Еще один момент: вы можете использовать контроллер представления для захвата события встряхивания, если вы не хотите без необходимости создавать подкласс UIView. Я знаю, что это не так уж и сложно, но вариант все же есть. Просто переместите фрагменты кода, которые Кендалл поместил в подкласс UIView, в ваш контроллер и отправьте сообщения becomeFirstResponder и resignFirstResponder в self вместо подкласса UIView.

    person Newtz    schedule 18.08.2009
    comment
    Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением. - person Alex Salauyou; 21.04.2015
    comment
    @SashaSalauyou Я четко сказал в самом первом предложении здесь, у меня не было достаточно репутации, чтобы оставлять комментарий в то время - person Newtz; 22.04.2015
    comment
    извиняюсь. Возможно, здесь 5-летний ответ попадет в очередь на рассмотрение)) - person Alex Salauyou; 22.04.2015

    Во-первых, я знаю, что это старый пост, но он все еще актуален, и я обнаружил, что два самых популярных ответа не обнаружили встряску как можно раньше. Вот как это сделать:

    1. Свяжите CoreMotion с вашим проектом на этапах сборки цели: CoreMotion
    2. В вашем ViewController:

      - (BOOL)canBecomeFirstResponder {
          return YES;
      }
      
      - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
      {
          if (motion == UIEventSubtypeMotionShake) {
              // Shake detected.
          }
      }
      
    person Dennis    schedule 13.03.2015

    Самое простое решение - создать новое корневое окно для вашего приложения:

    @implementation OMGWindow : UIWindow
    
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
        if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
            // via notification or something   
        }
    }
    @end
    

    Затем в делегате вашего приложения:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        //…
    }
    

    Если вы используете раскадровку, это может быть сложнее, я не знаю точно, какой код вам понадобится в делегате приложения.

    person mxcl    schedule 12.07.2014
    comment
    И да, вы можете получить свой базовый класс в @implementation, начиная с Xcode 5. - person mxcl; 12.07.2014
    comment
    Это работает, я использую его в нескольких приложениях. Так что не уверен, почему он был отклонен. - person mxcl; 16.12.2014
    comment
    работал как шарм. Если вам интересно, вы можете переопределить класс окна следующим образом: stackoverflow.com/a/10580083/709975 - person Edu; 10.02.2015
    comment
    Будет ли это работать, когда приложение находится в фоновом режиме? Если нет другой идеи, как определить, когда в фоновом режиме? - person nr5; 11.05.2018
    comment
    Это, вероятно, сработает, если у вас установлен фоновый режим. - person mxcl; 15.05.2018

    Просто используйте эти три метода, чтобы сделать это

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

    для получения дополнительной информации вы можете проверить полный пример кода на странице там

    person Mashhadi    schedule 21.02.2013

    Быстрая версия, основанная на самом первом ответе!

    override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
        if ( event?.subtype == .motionShake )
        {
            print("stop shaking me!")
        }
    }
    
    person user3069232    schedule 10.09.2017

    В Swift 5 вы можете захватывать движение и проверять,

    override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
       if motion == .motionShake 
       {
          print("shaking")
       }
    }
    
    person Amit Baderia    schedule 25.01.2021

    Чтобы включить это приложение для всего приложения, я создал категорию в UIWindow:

    @implementation UIWindow (Utils)
    
    - (BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Do whatever you want here...
        }
    }
    
    @end
    
    person Mike    schedule 15.09.2017