当前位置:  开发笔记 > 编程语言 > 正文

如何检测有人摇动iPhone的时间?

如何解决《如何检测有人摇动iPhone的时间?》经验,为你挑选了12个好方法。

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



1> Kendall Helm..:

在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(甚至系统视图)转换为只需通过仅使用这些方法对视图进行子类化(然后在IB中选择此新类型而不是基本类型,或在分配时使用它)来获取抖动事件的视图视图).

在视图控制器中,您要将此视图设置为第一响应者:

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

不要忘记,如果您有其他视图从用户操作(如搜索栏或文本输入字段)成为第一响应者,您还需要在另一个视图辞职时恢复摇动视图第一响应者状态!

即使将applicationSupportsShakeToEdit设置为NO,此方法也有效.


这对我来说不太有用:我需要覆盖我的控制器的`viewDidAppear`而不是`viewWillAppear`.我不确定为什么; 也许视图需要可见才能开始接收震动事件吗?
@Kendall - UIViewControllers实现UIResponder并且在响应者链中.最顶端的UIWindow也是如此.http://developer.apple.com/library/ios/DOCUMENTATION/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW5
如果(event.subtype == UIEventSubtypeMotionShake),可以使用::不要使用,如果(运动== UIEventSubtypeMotionShake)

2> millenomi..:

从我的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  {
    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

该组合可防止震动事件多次触发,直到用户停止摇动.


注意我在下面添加了一个答案,为3.0提供了一个更简单的方法.
最好的答案,因为iOS 3.0`totionBegan`和`motionEnded`事件在检测震动的确切开始和结束方面不是非常精确或准确.这种方法可以让您尽可能精确.
如果他们在特定的轴上精确地摇动会发生什么?
UIAccelerometerDelegate*accelerometer:didAccelerate:*已在iOS5中弃用.

3> 小智..:

我终于使用了这个Undo/Redo Manager Tutorial中的代码示例.
这正是您需要做的:

在App的Delegate中设置applicationSupportsShakeToEdit属性:

    - (void)applicationDidFinishLaunching:(UIApplication *)application {

        application.applicationSupportsShakeToEdit = YES;

        [window addSubview:viewController.view];
        [window makeKeyAndVisible];
}

在视图控制器中添加/覆盖canBecomeFirstResponder,viewDidAppear:viewWillDisappear:方法:

-(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
    }
}








4> Joe D'Andrea..:

首先,肯德尔7月10日的回答是正确的.

现在......我想做类似的事情(在iPhone OS 3.0+中),只是在我的情况下,我想在应用程序范围内,所以我可以在发生震动时提醒应用程序的各个部分.这就是我最终做的事情.

首先,我将UIWindow子类化.这很容易.创建一个带有界面的新类文件MotionWindow : UIWindow(随意选择自己的,natch).添加如下方法:

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

更改@"DeviceShaken"为您选择的通知名称.保存文件.

现在,如果您使用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:视图控制器所关注的地方.确保您对震动事件的响应知道它是否"已在进行中".否则,如果设备连续摇动两次,您将遇到交通拥堵.这样,您可以忽略其他通知,直到您真正完成对原始通知的响应.

另外:你可以选择提示关闭motionBeganmotionEnded.由你决定.就我而言,其效果总是需要发生后,该装置是在休息(对启动时震动),所以我用motionEnded.尝试两者,看看哪一个更有意义......或检测/通知两者!

还有一个(好奇的?)观察:请注意,此代码中没有第一响应者管理的迹象.到目前为止,我只尝试使用Table View控制器,所有内容似乎都能很好地协同工作!我不能担保其他情况.

肯德尔等.al - 任何人都可以说为什么UIWindow子类可能会如此?是因为窗户位于食物链的顶端吗?



5> 小智..:

我发现这篇文章寻找一个"摇晃"的实现.millenomi的答案对我来说效果很好,虽然我正在寻找一些需要更多"摇动动作"才能触发的东西.我用int shakeCount替换为布尔值.我还重新实现了Objective-C中的L0AccelerationIsShaking()方法.您可以通过调整添加到shakeCount的ammount来调整所需的抖动量.我不确定我是否已经找到了最佳值,但到目前为止似乎运行良好.希望这有助于某人:

- (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)];



6> Dave Verwer..:

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

加速计中有一些不错的示例代码:didAccelerate:方法位于iPhone开发人员网站上可用的GLPaint示例中AppController.m的底部.



7> nhgrif..:

在iOS 8.3版本(可能更早)与雨燕,它作为压倒一切的简单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!")
    }
}



8> Benjamin Ort..:

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

#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



9> camflan..:

查看GLPaint示例.

http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html



10> Himanshu Mah..:

在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 
     }



11> Newtz..:

很抱歉发布这个作为答案而不是评论但是你可以看到我是Stack Overflow的新手,所以我还没有足够的信誉来发表评论!

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

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



12> Dennis..:

首先,我知道这是一个旧帖子,但它仍然相关,我发现两个最高投票的答案并没有尽早发现.这是怎么做的:

    在目标的构建阶段将CoreMotion链接到您的项目: CoreMotion

    在您的ViewController中:

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

推荐阅读
Chloemw
这个屌丝很懒,什么也没留下!