12

我有一个简单的问题。我试图检测用户何时摇动 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

4 回答 4

18

这是我找到的解决方案。这很好用,但你必须使用 deviceMotionUpdateInterval 时间值以及加速度阈值,这对于实际的“轻微抖动”与“拿起手机并将其靠近你的脸等”进行精细平衡可能会很棘手...” 可能有更好的方法,但这里有一个开始。在我的视图中 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];
        }
}
于 2013-10-13T07:28:49.113 回答
7

这是一个基于 zic10 答案的快速版本,添加了一个标志,即使该处理程序中的第一行是motionManager.stopDeviceMotionUpdates().

此外,3.0如果您想忽略抖动但检测到颠簸,则 around 值可能很有用。我发现0.3它太低了,因为它最终更像是“检测移动”。在我的测试中,范围更像:

  • 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 在模拟器中测试抖动。

于 2016-04-22T22:28:58.407 回答
6

使用核心运动。将您的二进制文件与 CoreMotion 框架链接。在您的课程中包含#import。创建 CMMotionManager 的实例。将 deviceMotionUpdateInterval 属性设置为合适的值。然后调用 startDeviceMotionUpdatesToQueue。您将在块内获得持续更新,包括加速度、磁场、旋转等。您将获得所需的数据。需要注意的一件事是,如果间隔太小,更新将非常迅速,因此您必须采用适当的逻辑来处理相同的问题。

于 2013-10-11T06:16:45.537 回答
1

下面是我如何使用 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

}
于 2017-02-24T21:40:20.337 回答