Обнаружение движения 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
}