344

誰かがiPhoneを振ったときに反応したい。どのように振ったかは特に気にしませんが、ほんの一瞬激しく振られただけです。これを検出する方法を知っている人はいますか?

4

17 に答える 17

180

私の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

ヒステリシスは、ユーザーがシェイクを停止するまで、シェイク イベントが複数回トリガーされるのを防ぎます。

于 2008-10-01T20:43:58.170 に答える
154

最終的に、このUndo/Redo Manager Tutorialのコード例を使用して機能させました。
これはまさにあなたがする必要があることです:

  • アプリのデリゲートでapplicationSupportsShakeToEditプロパティを設定します。
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • View Controller にcanBecomeFirstResponderviewDidAppear:、およびviewWillDisappear:メソッドを追加/オーバーライドします。
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    

  • ビュー コントローラーにmotionEndedメソッドを追加します。
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    
    于 2010-03-08T23:58:19.530 に答える
    94

    まず、Kendall の 7 月 10 日の回答は的を射ています。

    今...私は(iPhone OS 3.0+で)同様のことをしたかったのですが、私の場合にのみアプリ全体でそれを望んでいたので、揺れが発生したときにアプリのさまざまな部分に警告することができました. これが私がやったことです。

    まず、UIWindowをサブクラス化しました。これは簡単です。次のようなインターフェイスで新しいクラス ファイルを作成しますMotionWindow : UIWindow(自由に独自のものを選択してください)。次のようにメソッドを追加します。

    - (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:に私のものを置きます。シェイクイベントへの応答で、それが「すでに進行中」かどうかを確認してください。そうしないと、デバイスを 2 回続けて振ると、交通渋滞が発生します。このようにして、元の通知への応答が本当に完了するまで、他の通知を無視できます。

    また: motionBegan と motionEnded の合図を選択することもできます。それはあなた次第です。私の場合、エフェクトは常にデバイスが静止した後 (振動が始まったときとは異なります) に発生する必要があるため、 motionEnded を使用ます。両方を試して、どちらがより理にかなっているのかを確認してください...または両方を検出/通知してください!

    ここでもう 1 つ (興味深い?) 所見: このコードにはファーストレスポンダ管理の兆候がないことに注意してください。これまでのところ、Table View Controller でのみこれを試しましたが、すべてがうまく連携しているようです。ただし、他のシナリオについては保証できません。

    ケンダル他 アル - UIWindowサブクラスの場合、これがなぜそうなのか誰でも話せますか? 食物連鎖の頂点に窓があるからでしょうか。

    于 2009-08-29T13:47:43.933 に答える
    35

    「揺れる」実装を探してこの投稿に出くわしました。millenomiの答えは私にとってはうまくいきましたが、トリガーするためにもう少し「シェイクアクション」が必要なものを探していました. ブール値をintのshakeCountに置き換えました。また、Objective-C で L0AccelerationIsShaking() メソッドを再実装しました。シェイクカウントに追加される量を微調整することで、必要なシェイクの量を微調整できます。まだ最適な値が見つかったかどうかはわかりませんが、これまでのところうまく機能しているようです。これが誰かに役立つことを願っています:

    - (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)];
    
    于 2008-11-10T17:14:52.160 に答える
    12

    UIAccelerometerDelegate プロトコルの一部である accelerometer:didAccelerate: メソッドを介して加速度計をチェックし、値がシェイクに必要な移動量のしきい値を超えているかどうかをチェックする必要があります。

    iPhone 開発者サイトで入手できる GLPaint の例の AppController.m の一番下にある accelerometer:didAccelerate: メソッドに適切なサンプル コードがあります。

    于 2008-09-29T21:17:44.377 に答える
    10

    これは、必要な基本的なデリゲート コードです。

    #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>

    于 2009-03-22T21:15:52.407 に答える
    7

    GLPaint の例を確認してください。

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

    于 2008-09-29T20:16:00.327 に答える
    7

    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 
         }
    
    于 2012-10-15T12:11:16.310 に答える
    5

    これをコメントではなく回答として投稿して申し訳ありませんが、ご覧のとおり、私はStack Overflowを初めて使用するため、コメントを投稿するほど評判が良くありません!

    とにかく、ビューがビュー階層の一部になったら、最初のレスポンダーのステータスを確実に設定することについて@cireに2番目に言います。したがって、たとえば、View Controllers メソッドでファーストレスポンダーのステータスを設定してviewDidLoadも機能しません。そして、それが機能しているかどうかわからない場合は[view becomeFirstResponder]、テストできるブール値を返します。

    別のポイント:不必要に UIView サブクラスを作成したくない場合は、View Controller を使用してシェイク イベントをキャプチャできますそれほど面倒ではないことはわかっていますが、それでもオプションがあります。Kendall が UIView サブクラスに入れたコード スニペットをコントローラーに移動し、 UIView サブクラスの代わりにbecomeFirstResponderresignFirstResponderメッセージを送信するだけです。self

    于 2009-08-18T15:05:41.763 に答える
    1

    これらの3つの方法を使用してください

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

    詳細については、あちらの完全なサンプルコードを確認してください。

    于 2013-02-21T09:30:47.530 に答える