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

时间:2020-03-06 14:53:38  来源:igfitidea点击:

当有人摇动iPhone时,我想做出反应。我并不特别在乎他们如何摇动它,只是它剧烈地挥舞了一秒钟。有人知道如何检测吗?

解决方案

查看GLPaint示例。

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

我们需要通过accelerometer:didAccelerate:方法检查加速度计,该方法是UIAccelerometerDelegate协议的一部分,并检查该值是否超过摇动所需的运动量阈值。

在GLPaint示例中AppController.m底部的accelerometer:didAccelerate:方法中有不错的示例代码,该示例代码可从iPhone开发人员网站上获得。

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

历史记录可防止震动事件多次触发,直到用户停止震动为止。

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

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

这是我们需要的基本委托代码:

#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];   
    }

还要在接口中的相应代码中设置。 IE:

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

在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中选择此新类型而不是基本类型,或者在分配a时使用它)来获得震动事件的视图看法)。

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

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

不要忘记,如果我们有其他视图已成为用户操作的第一响应者(例如搜索栏或者文本输入字段),则当其他视图退出时,我们还需要恢复摇动视图第一响应者的状态!

即使将applicationSupportsShakeToEdit设置为NO,此方法也可以使用。

很抱歉将其发布为答案,而不是发表评论,但是我们可以看到我是Stack Overflow的新手,所以我还没有足够的声誉发表评论!

无论如何,一旦视图成为视图层次结构的一部分,我都会确保确保设置第一响应者状态。因此,例如,无法在视图控制器的viewDidLoad方法中设置第一响应者状态。而且,如果我们不确定它是否正常工作,[view viewFirstFirstResponder]会返回一个布尔值供我们测试。

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

首先,肯德尔(Kendall)在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:放在与View Controller有关的地方。确保我们对震动事件的响应知道它是否"已在进行中"。否则,如果将设备连续晃动两次,将会造成交通拥堵。这样,我们可以忽略其他通知,直到我们真正完成对原始通知的响应为止。

另外:我们可以选择从motionBegan到motionEnded进行提示。由你决定。就我而言,效果总是需要在设备静止后才发生(相对于它开始晃动时),因此我使用motionEnded。尝试两者,看看哪个更有意义...或者同时检测/通知两者!

这里还有一个(奇怪的?)观察结果:请注意,此代码中没有急救人员管理的迹象。到目前为止,我仅使用Table View Controller进行了尝试,并且一切似乎都可以很好地协同工作!我不能保证其他情况。

肯德尔(Kendall)等人。有谁能说出为什么UIWindow子类会如此?是因为窗口位于食物链的顶部吗?

我最终使用此"撤消/重做管理器教程"中的代码示例使其工作。
这正是我们需要执行的操作:

  • 在应用程序的委托中设置applicationSupportsShakeToEdit属性:
- (void)applicationDidFinishLaunching:(UIApplication *)application {

        application.applicationSupportsShakeToEdit = YES;

        [window addSubview:viewController.view];
        [window makeKeyAndVisible];
}
  • 在View Controller中添加/覆盖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
    }
}