Как я обнаруживаю, когда кто-то встряхивает iPhone?

339
задан James Webster 7 August 2015 в 03:47
поделиться

9 ответов

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.

292
ответ дан 23 November 2019 в 00:36
поделиться
7
ответ дан logancautrell 23 November 2019 в 00:36
поделиться

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

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

12
ответ дан Dave Verwer 23 November 2019 в 00:36
поделиться

Я столкнулся с этим сообщением, ища "дрожащую" реализацию. ответ millenomi работал хорошо на меня, хотя я искал что-то, что потребовало, чтобы немного больше "дрожащего действия" инициировало. Я заменил к булеву значению интервалом 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);
}

пз: я установил интервал обновления на 1/15-ю из секунды.

[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
34
ответ дан 23 November 2019 в 00:36
поделиться

От моего приложение 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

гистерезис препятствует тому, чтобы событие встряски инициировало многократно, пока пользователь не останавливает встряску.

179
ответ дан millenomi 23 November 2019 в 00:36
поделиться

Это - основной код делегата, в котором Вы нуждаетесь:

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

9
ответ дан me1974 23 November 2019 в 00:36
поделиться

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

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

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

5
ответ дан 23 November 2019 в 00:36
поделиться

Во-первых, ответ Кендалла от 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 vs. motionEnded . Тебе решать. В моем случае эффект всегда должен иметь место после того, как устройство находится в состоянии покоя (а не когда оно начинает дрожать), поэтому я использую motionEnded . Попробуйте оба и посмотрите, какой из них имеет больше смысла ... или обнаружите / уведомите для обоих!

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

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

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

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

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

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

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

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

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

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

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

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

94
ответ дан 23 November 2019 в 00:36
поделиться

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

  • Установите свойство 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
        }
    }
    
    154
    ответ дан 23 November 2019 в 00:36
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: