iOS运动检测:运动检测灵敏度级别

13

我有一个简单的问题。我正在尝试检测用户何时晃动iPhone。我已经准备好检测动作的标准代码,没有任何问题。然而,在我的实际手机上测试时,我意识到必须用相当大的力量摇晃设备才能触发运动检测。我想知道是否有一种方法来实现灵敏度检查。例如,一种检测用户轻轻地摇动设备或在轻微和强烈摇动之间的方法。这将针对iOS 7,因此非常感谢任何未过时的iOS版本的建议和提示。我已经搜索了,但尚未找到任何解决此问题的好方法(如果有的话)。

谢谢!

-(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];
}
4个回答

18

这是我找到的解决方案。它运作良好,但你需要调整deviceMotionUpdateInterval时间值以及加速度阈值,这可能有点棘手,要实现一个真正的“轻晃”与“把手机拿起来靠近脸等”的平衡。也许还有更好的方法,但这是一个开始。在我的view 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];
        }
}

在摇晃设备时,该方法motionMethod没有被调用。 - Dhara
你在AppDelegate.m文件中的didFinishLaunchingWithOptions方法中是否包含了application.applicationSupportsShakeToEdit = YES;的语句? - zic10
5
需要将CMMotionManager实例声明为属性,以便使其保留在内存中... - Harshit Gupta

7
这是一个基于zic10答案的Swift版本,增加了一个标志,可以防止在处理程序的第一行是时获取一些额外的调用。
此外,如果您想忽略晃动但检测到碰撞,则约3.0的值可能会很有用。在我的测试中,范围更像是:
0.75 - 2.49是更好的晃动灵敏度范围
2.5 - 5.0是“忽略晃动,检测碰撞”的良好范围
以下是Xcode单个VC模板的完整视图控制器:
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的快照工具一样使用晃动。可以尝试如下代码:

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在模拟器中测试晃动效果。


6

使用 core motion

将您的二进制文件与 CoreMotion 框架链接起来。 在您的类中包含

#import <CoreMotion/CoreMotion.h>

创建一个 CMMotionManager 实例。

deviceMotionUpdateInterval 属性设置为适当的值。 然后调用 startDeviceMotionUpdatesToQueue

您将在块内获得连续的更新,其中包括加速度、磁场、旋转等 您将获得所需的数据。

需要注意的一点是,如果间隔太小,更新将非常快。 因此,您将不得不采用适当的逻辑来处理它。


谢谢,这对我帮助很大。我会发表一个解决方案,该方案是在您的帖子和其他一些谷歌搜索的指引下实现的。 - zic10
当应用程序在后台运行时,是否可能检测到摇晃事件? - Rajan Maheshwari

3
这是我使用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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接