Обнаружение движения iOS: уровни чувствительности обнаружения движения

У меня есть простой вопрос. Я пытаюсь обнаружить, когда пользователь встряхивает iPhone. У меня есть стандартный код для обнаружения движения, и это не проблема. Однако, тестируя это на моем фактическом телефоне, я понял, что вам нужно сильно встряхнуть устройство, чтобы заставить детектор движения срабатывать. Я хотел бы знать, есть ли способ реализовать уровень проверки чувствительности. Например, способ обнаружить, если пользователь слегка встряхивает устройство или где-то между легким и жестким встряхиванием. Это будет нацелено на iOS 7, поэтому все советы или рекомендации, которые не устарели от старой версии iOS, будут очень признательны. Я сделал свой поиск в Google, но пока не нашел хороших решений этой проблемы (если они есть.)

Спасибо!

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

Ответ 1

Используйте движение ядра. Свяжите свой двоичный файл с каркасом CoreMotion. Включите #import в свой класс. Создайте экземпляр CMMotionManager. Задайте свойство deviceMotionUpdateInterval подходящему значению. Затем вызовите startDeviceMotionUpdatesToQueue. Вы получите непрерывные обновления внутри блока, включая ускорение, магнитное поле, вращение и т.д. Вы получите требуемые данные. Одна вещь, о которой следует заботиться, заключается в том, что обновление должно быть настолько быстрым, если интервал слишком мал, и, следовательно, вам придется использовать подходящую логику для обработки того же самого.

Ответ 2

Вот решение, которое я нашел. Это работает хорошо, но вам нужно играть с параметром timeMotionUpdateInterval time, а также с ускорением Threshold, который может быть сложным, чтобы получить прекрасный эффект балансировки для фактического "легкого встряхивания" против "сбора телефона и перемещения его ближе к вашему лицу и т.д...." Там могут быть лучшие способы, но здесь нужно начать. Внутри моего представления didLoad я сделал что-то вроде этого:

#import <CoreMotion/CoreMotion.h> //do not forget to link the CoreMotion framework to your project
#define accelerationThreshold  0.30 // or whatever is appropriate - play around with different values

-(void)viewDidLoad
{
      CMMotionManager *motionManager; 

      motionManager = [[CMMotionManager alloc] init];
      motionManager.deviceMotionUpdateInterval = 1;

      [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]  withHandler:^(CMDeviceMotion *motion, NSError *error)
             {
                [self motionMethod:motion];
             }];
}

-(void)motionMethod:(CMDeviceMotion *)deviceMotion
{
    CMAcceleration userAcceleration = deviceMotion.userAcceleration;
    if (fabs(userAcceleration.x) > accelerationThreshold
        || fabs(userAcceleration.y) > accelerationThreshold
        || fabs(userAcceleration.z) > accelerationThreshold)
        {
           //Motion detected, handle it with method calls or additional
           //logic here.
           [self foo];
        }
}

Ответ 3

Это быстрая версия, основанная на ответе zic10, с добавлением флага, который предотвращает получение нескольких дополнительных вызовов вашему обработчику движения, даже если первая строка в этом обработчике motionManager.stopDeviceMotionUpdates().

Кроме того, значение вокруг 3.0 может быть полезно, если вы хотите игнорировать дрожание, но обнаруживаете удар. Я обнаружил, что 0.3 слишком низкое, поскольку оно больше похоже на "обнаружение движения". В моих тестах диапазоны были больше похожими:

  • 0,75 - 2,49 - лучший диапазон для чувствительности к вибрации
  • 2.5 - 5.0 - хороший диапазон для "игнорирования дрожания, обнаружения ударов"

Вот полный контроллер представления для одного шаблона VC Xcode:

import UIKit
import CoreMotion

class ViewController: UIViewController {

    lazy var motionManager: CMMotionManager = {
        return CMMotionManager()
    }()

    let accelerationThreshold = 3.0

    var handlingShake = false

    override func viewWillAppear(animated: Bool) {
        handlingShake = false
        motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.currentQueue()!) { [weak self] (motion, error) in
            if
                let userAcceleration = motion?.userAcceleration,
                let _self = self {

                print("\(userAcceleration.x) / \(userAcceleration.y)")

                if (fabs(userAcceleration.x) > _self.accelerationThreshold
                    || fabs(userAcceleration.y) > _self.accelerationThreshold
                    || fabs(userAcceleration.z) > _self.accelerationThreshold)
                {
                    if !_self.handlingShake {
                        _self.handlingShake = true
                        _self.handleShake();
                    }
                }

            } else {
                print("Motion error: \(error)")
            }
        }
    }

    override func viewWillDisappear(animated: Bool) {
        // or wherever appropriate
        motionManager.stopDeviceMotionUpdates()
    }

    func handleShake() {
        performSegueWithIdentifier("showShakeScreen", sender: nil)
    }

}

И раскадровка, которую я использовал для этого теста, выглядит следующим образом:

введите описание изображения здесь

Также стоит отметить, что CoreMotion не тестируется в симуляторе. Из-за этого ограничения вы, возможно, все же найдете целесообразным дополнительно реализовать метод UIDevice для обнаружения дрожания. Это позволит вам вручную протестировать тряску в симуляторе или дать UITests доступ к встряхиванию для тестирования или инструменты, такие как fastlane snapshot. Что-то вроде:

class ViewController: UIViewController {

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        becomeFirstResponder()
    }

    override func canBecomeFirstResponder() -> Bool {
        return true
    }

    override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
        if TARGET_OS_SIMULATOR != 0 {
            if event?.subtype == .MotionShake {
                // do stuff
            }
        }
    }

}

И затем используйте Ctrl-Cmd-Z, чтобы проверить тряску в симуляторе.

Ответ 4

Вот как я это сделал, используя Swift 3.

Импортировать CoreMotion и создать экземпляр

import CoreMotion
let motionManager = CMMotionManager()

В ViewDidLoad или где вы хотите начать проверку обновлений:

 motionManager.startDeviceMotionUpdates(to: OperationQueue.current!, withHandler:{
            deviceManager, error in
            if(error == nil){
                if let mgr = deviceManager{
                    self.handleMotion(rate: mgr.rotationRate)
                }
            }
        })

Эта функция принимает скорость вращения и получает сумму для абсолютных значений для движений x, y и z

  func handleMotion(rate: CMRotationRate){
        let totalRotation = abs(rate.x) + abs(rate.y) + abs(rate.z)

        if(totalRotation > 20) {//Play around with the number 20 to find the optimal level for your case
            start()
        }else{
       print(totalRotation)
        }
    }

func start(){

//The function you want to trigger when the device is rotated

}