我有一个简单的问题。我试图检测用户何时摇动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];
}
答案 0 :(得分:18)
这是我找到的解决方案。这很好但你必须使用deviceMotionUpdateInterval时间值以及accelerationThreshold,这对于实际的“轻微摇动”与“拿起手机并将其移近您的脸部等”进行精细平衡可能很棘手......“可能有更好的方法,但这里有一个开始。在我的视图中,我做过这样的事情:
#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];
}
}
答案 1 :(得分:6)
使用核心动作。 将您的二进制文件链接到CoreMotion框架。 在你的班级中加入#import。 创建CMMotionManager的实例。 将deviceMotionUpdateInterval属性设置为合适的值。 然后调用startDeviceMotionUpdatesToQueue。 您将在块内获得持续更新,包括加速度,磁场,旋转等。 您将获得所需的数据。 要注意的一件事是,如果间隔太小,更新应该如此之快,因此你必须采用合适的逻辑来处理相同的事情。
答案 2 :(得分:6)
这是一个基于zic10的答案的快速版本,添加了一个标志,即使该处理程序中的第一行是motionManager.stopDeviceMotionUpdates()
,也会阻止对您的动作处理程序进行一些额外调用。< / p>
此外,如果您想忽略抖动但检测到碰撞,则大约3.0
的值会很有用。我发现0.3
太低了,因为它最终更像是&#34;检测到移动&#34;。在我的测试中,范围更像是:
以下是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在模拟器中测试抖动。
答案 3 :(得分:0)
下面是我如何使用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
}