如何检测到有人摇晃iPhone?

我想在有人摇动iPhone时做出反应。 我不是特别在乎他们是怎么动摇的,只是在一瞬间大力挥手。 有谁知道如何检测这个?

在3.0中,现在有一个更简单的方法 – 钩入新的运动事件。

主要的技巧是你需要有一些UIView(而不是UIViewController),你希望它作为firstResponder接收摇动事件消息。 以下是您可以在任何UIView中使用的代码来获取震动事件:

@implementation ShakingView - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if ( event.subtype == UIEventSubtypeMotionShake ) { // Put in code here to handle shake } if ( [super respondsToSelector:@selector(motionEnded:withEvent:)] ) [super motionEnded:motion withEvent:event]; } - (BOOL)canBecomeFirstResponder { return YES; } @end 

你可以很容易地将任何UIView(甚至是系统视图)转换成一个视图,只需通过这些方法的subclassing视图就可以获得shake事件(然后在IB中select这个新types而不是基types,或者在分配视图)。

在视图控制器中,你要设置这个视图成为第一响应者:

 - (void) viewWillAppear:(BOOL)animated { [shakeView becomeFirstResponder]; [super viewWillAppear:animated]; } - (void) viewWillDisappear:(BOOL)animated { [shakeView resignFirstResponder]; [super viewWillDisappear:animated]; } 

不要忘记,如果您有其他视图成为用户操作的第一响应者(如search栏或文本input字段),则还需要在另一视图退出时恢复摇动视图第一响应者状态!

即使您将applicationSupportsShakeToEdit设置为NO,此方法仍然有效。

从我的Diceshaker应用程序:

 // Ensures the shake is strong enough on at least two axes before declaring it a shake. // "Strong enough" means "greater than a client-supplied threshold" in G's. static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) { double deltaX = fabs(last.x - current.x), deltaY = fabs(last.y - current.y), deltaZ = fabs(last.z - current.z); return (deltaX > threshold && deltaY > threshold) || (deltaX > threshold && deltaZ > threshold) || (deltaY > threshold && deltaZ > threshold); } @interface L0AppDelegate : NSObject <UIApplicationDelegate> { BOOL histeresisExcited; UIAcceleration* lastAcceleration; } @property(retain) UIAcceleration* lastAcceleration; @end @implementation L0AppDelegate - (void)applicationDidFinishLaunching:(UIApplication *)application { [UIAccelerometer sharedAccelerometer].delegate = self; } - (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { if (self.lastAcceleration) { if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) { histeresisExcited = YES; /* SHAKE DETECTED. DO HERE WHAT YOU WANT. */ } else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) { histeresisExcited = NO; } } self.lastAcceleration = acceleration; } // and proper @synthesize and -dealloc boilerplate code @end 

通过避免震动,可以防止震动事件多次触发,直到用户停止震动为止。

我终于使用这个撤销/重做pipe理器教程的代码示例工作。
这正是你需要做的:

  • 应用程序的代理中设置applicationSupportsShakeToEdit属性:
  •  - (void)applicationDidFinishLaunching:(UIApplication *)application { application.applicationSupportsShakeToEdit = YES; [window addSubview:viewController.view]; [window makeKeyAndVisible]; } 
  • 添加/覆盖canBecomeFirstResponderviewDidAppear:viewWillDisappear: View Controller中的方法:
  •  -(BOOL)canBecomeFirstResponder { return YES; } -(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self becomeFirstResponder]; } - (void)viewWillDisappear:(BOOL)animated { [self resignFirstResponder]; [super viewWillDisappear:animated]; } 
  • motionEnded方法添加到您的View Controller中:
  •  - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // your code } } 

    首先,肯德尔7月10日的答案是现场。

    现在…我想要做类似的事情(在iPhone OS 3.0以上),只有在我的情况下,我希望它的应用程序范围内,所以我可以提醒应用程序的各个部分发生了摇一摇。 这是我最终做的。

    首先,我分类了UIWindow 。 这很容易。 使用MotionWindow : UIWindow等界面创build一个新的类文件MotionWindow : UIWindow (随意挑选你自己的natch)。 添加一个像这样的方法:

     - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) { [[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self]; } } 

    @"DeviceShaken"更改为您select的通知名称。 保存文件。

    现在,如果您使用MainWindow.xib(库存Xcode模板),请进入该窗口并将您的Window对象的类从UIWindow更改为MotionWindow或您调用它的任何内容。 保存xib。 如果以编程方式设置UIWindow ,则可以使用新的Window类。

    现在你的应用程序正在使用专门的UIWindow类。 无论你想被告知摇一摇,注册他们的通知! 喜欢这个:

     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil]; 

    以观察者身份去除自己:

     [[NSNotificationCenter defaultCenter] removeObserver:self]; 

    我把我的viewWillAppear: viewWillDisappear:视图控制器的关注。 确保你对震动事件的反应知道它是否“已经在进行”。 否则,如果连续两次摇动设备,您将会遇到交通堵塞。 这样,您可以忽略其他通知,直到您真正完成对原始通知的响应。

    另外:你可以select提示motionBeganmotionEnded 。 随你便。 在我的情况下,效果总是需要在设备rest(相对于开始晃动)之后进行,所以我使用motionEnded 。 尝试两个,看看哪一个更有意义…或检测/通知两个!

    这里还有一个(好奇的)观察:注意在这个代码中没有第一响应者pipe理的迹象。 我只使用表视图控制器到目前为止,一切似乎一起工作很好! 虽然我不能担保其他情况。

    肯德尔,等。 铝 – 谁能说出为什么这可能是这样的UIWindow子类? 是因为窗户在食物链的顶端吗?

    我遇到这个post寻找一个“摇晃”的实现。 millenomi的回答对我来说效果不错,虽然我在寻找一些需要多一点“动作”才能触发的东西。 我用一个int shakeCountreplace为布尔值。 我还重新实现了Objective-C中的L0AccelerationIsShaking()方法。 您可以通过调整添加到shakeCount的ammount来调整所需的震动。 我不确定我是否已经find了最佳的价值,但目前看起来效果不错。 希望这可以帮助某人:

     - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { if (self.lastAcceleration) { if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7] && shakeCount >= 9) { //Shaking here, DO stuff. shakeCount = 0; } else if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7]) { shakeCount = shakeCount + 5; }else if (![self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.2]) { if (shakeCount > 0) { shakeCount--; } } } self.lastAcceleration = acceleration; } - (BOOL) AccelerationIsShakingLast:(UIAcceleration *)last current:(UIAcceleration *)current threshold:(double)threshold { double deltaX = fabs(last.x - current.x), deltaY = fabs(last.y - current.y), deltaZ = fabs(last.z - current.z); return (deltaX > threshold && deltaY > threshold) || (deltaX > threshold && deltaZ > threshold) || (deltaY > threshold && deltaZ > threshold); } 

    PS:我把更新间隔设置为1/15秒。

     [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)]; 

    您需要通过加速计检查加速度计:didAccelerate:作为UIAccelerometerDelegate协议一部分的方法,并检查这些值是否超过了抖动所需移动量的阈值。

    在Accelerometer中有一些体面的示例代码:在iPhone开发人员网站上可用的GLPaint示例中的AppController.m底部的didAccelerate:方法。

    在iOS 8.3(或许更早)中,使用Swift就像在视图控制器中重写motionBeganmotionEnded方法一样简单:

     class ViewController: UIViewController { override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent) { println("started shaking!") } override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) { println("ended shaking!") } } 

    这是您需要的基本代表代码:

     #define kAccelerationThreshold 2.2 #pragma mark - #pragma mark UIAccelerometerDelegate Methods - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { if (fabsf(acceleration.x) > kAccelerationThreshold || fabsf(acceleration.y) > kAccelerationThreshold || fabsf(acceleration.z) > kAccelerationThreshold) [self myShakeMethodGoesHere]; } 

    还要在接口中设置适当的代码。 即:

    @interface MyViewController:UIViewController <UIPickerViewDelegate,UIPickerViewDataSource,UIAccelerometerDelegate>

    在ViewController.m文件中添加以下方法,其工作正常

      -(BOOL) canBecomeFirstResponder { /* Here, We want our view (not viewcontroller) as first responder to receive shake event message */ return YES; } -(void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if(event.subtype==UIEventSubtypeMotionShake) { // Code at shake event UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Motion" message:@"Phone Vibrate"delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil]; [alert show]; [alert release]; [self.view setBackgroundColor:[UIColor redColor]]; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self becomeFirstResponder]; // View as first responder } 

    抱歉发表这个答案,而不是一个评论,但你可以看到我是堆栈溢出新,所以我还没有足够的信誉发表评论!

    无论如何,我第二@cire关于确保一旦视图是视图层次结构的一部分设置第一响应者状态。 所以在视图控制器viewDidLoad方法中设置第一个响应者状态将不起作用。 如果你不确定它是否工作[view becomeFirstResponder]返回你一个你可以testing的布尔值。

    另一点:如果你不想不必要地创build一个UIView子类,你可以使用一个视图控制器来捕获shake事件。 我知道这不是那么麻烦,但仍然有select。 只要将Kendall放入UIView子类的代码片段移动到您的控制器中,然后将becomeFirstResponderresignFirstResponder消息发送给self而不是UIView子类。

    首先,我知道这是一个旧的post,但它仍然是相关的,我发现最高的两个答案没有尽早检测到这个问题 。 这是如何做到这一点:

    1. 将CoreMotion链接到目标构build阶段的项目: CoreMotion
    2. 在你的ViewController中:

       - (BOOL)canBecomeFirstResponder { return YES; } - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // Shake detected. } } 

    最简单的解决scheme是为您的应用程序派生一个新的根窗口:

     @implementation OMGWindow : UIWindow - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) { // via notification or something } } @end 

    然后在你的应用程序委托中:

     - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; //… } 

    如果您正在使用故事板,这可能会更棘手,我不知道你将需要在应用程序委托精确的代码。

    只要用这三种方法就可以了

     - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{ - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{ - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{ 

    有关详细信息,您可以在那里查看完整的示例代码

    基于第一个答案的swiftease版本!

     override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) { if ( event?.subtype == .motionShake ) { print("stop shaking me!") } } 

    为了启用这个应用程序,我在UIWindow上创build了一个类别:

     @implementation UIWindow (Utils) - (BOOL)canBecomeFirstResponder { return YES; } - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // Do whatever you want here... } } @end