346 Stimmen

Wie erkenne ich, wenn jemand ein iPhone schüttelt?

Ich möchte reagieren, wenn jemand das iPhone schüttelt. Dabei ist es mir egal, wie es geschüttelt wird, sondern nur, dass es für den Bruchteil einer Sekunde heftig geschüttelt wurde. Weiß jemand, wie man das erkennen kann?

293voto

In Version 3.0 gibt es jetzt einen einfacheren Weg - die neuen Bewegungsereignisse.

Der Haupttrick ist, dass Sie eine UIView (nicht UIViewController) haben müssen, die als firstResponder die Shake-Ereignisnachrichten empfangen soll. Hier ist der Code, den Sie in jeder UIView verwenden können, um Shake-Events zu erhalten:

@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

Sie können jede UIView (sogar System-Views) leicht in eine View umwandeln, die das Shake-Ereignis abrufen kann, indem Sie die View nur mit diesen Methoden subklassifizieren (und dann diesen neuen Typ anstelle des Basistyps in IB auswählen oder ihn beim Zuweisen einer View verwenden).

Im View-Controller wollen Sie diese Ansicht zum First Responder machen:

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

Vergessen Sie nicht, dass Sie, wenn Sie andere Ansichten haben, die durch Benutzeraktionen zum First Responder werden (z. B. eine Suchleiste oder ein Texteingabefeld), auch den First Responder-Status der Schüttelansicht wiederherstellen müssen, wenn die andere Ansicht zurücktritt!

Diese Methode funktioniert auch, wenn Sie applicationSupportsShakeToEdit auf NO setzen.

180voto

millenomi Punkte 6561

Von meinem Diceshaker Anwendung:

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

Die Histerese verhindert, dass das Schüttelereignis mehrfach ausgelöst wird, bis der Benutzer das Schütteln beendet.

154voto

Eran Talmor Punkte 1918

Ich habe es schließlich zum Laufen gebracht, indem ich Codebeispiele aus diesem Tutorial zum Undo/Redo Manager .
Das ist genau das, was Sie tun müssen:

  • Setzen Sie die applicationSupportsShakeToEdit Eigenschaft im Delegaten der App:

    - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
        application.applicationSupportsShakeToEdit = YES;
    
        [window addSubview:viewController.view];
        [window makeKeyAndVisible];

    }

  • Hinzufügen/Aufheben canBecomeFirstResponder , viewDidAppear: y viewWillDisappear: Methoden in Ihrem View Controller:

    -(BOOL)canBecomeFirstResponder { return YES; }

    -(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self becomeFirstResponder]; }

    • (void)viewWillDisappear:(BOOL)animated { [self resignFirstResponder]; [super viewWillDisappear:animated]; }
  • Fügen Sie die motionEnded Methode zu Ihrem View Controller hinzufügen:

    • (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // your code } }

94voto

Joe D'Andrea Punkte 5121

Erstens: Kendalls Antwort vom 10. Juli ist genau richtig.

Nun ... Ich wollte etwas Ähnliches machen (in iPhone OS 3.0+), nur in meinem Fall wollte ich es app-weit, so dass ich alarmieren konnte verschiedene Teile der App, wenn eine Erschütterung auftrat. Hier ist, was ich am Ende tat.

Zunächst habe ich die Unterklasse UIWindow . Das ist kinderleicht. Erstellen Sie eine neue Klassendatei mit einer Schnittstelle wie MotionWindow : UIWindow (Sie können natürlich auch Ihre eigenen auswählen). Fügen Sie eine Methode wie folgt hinzu:

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

Ändern Sie @"DeviceShaken" auf den Namen der Meldung Ihrer Wahl. Speichern Sie die Datei.

Wenn Sie eine MainWindow.xib (Standard-Xcode-Vorlage) verwenden, ändern Sie dort die Klasse des Window-Objekts von UIWindow まで BewegungsFenster oder wie auch immer Sie es nennen. Speichern Sie die xib. Wenn Sie die UIWindow programmieren, verwenden Sie dort stattdessen Ihre neue Fensterklasse.

Jetzt verwendet Ihre Anwendung die spezialisierte UIWindow Klasse. Wo immer Sie über einen Shake informiert werden möchten, melden Sie sich für die Benachrichtigungen an! Zum Beispiel so:

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

Sich selbst als Beobachter zu entfernen:

[[NSNotificationCenter defaultCenter] removeObserver:self];

Ich habe meine in viewWillAppear: y viewWillDisappear: wenn es um View-Controller geht. Vergewissern Sie sich, dass Ihre Antwort auf das Schüttelereignis weiß, ob es "bereits im Gange" ist oder nicht. Wenn das Gerät zweimal hintereinander geschüttelt wird, kommt es sonst zu einem kleinen Stau. Auf diese Weise können Sie andere Benachrichtigungen ignorieren, bis Sie wirklich auf die ursprüngliche Benachrichtigung geantwortet haben.

Auch: Sie können sich entscheiden, ob Sie den Cue von motionBegan vs. motionEnded . Es liegt an Ihnen. In meinem Fall muss der Effekt immer stattfinden nach das Gerät in Ruhe ist (im Gegensatz zu dem Moment, in dem es anfängt zu wackeln), also verwende ich motionEnded . Probieren Sie beides aus und sehen Sie, was mehr Sinn macht ... oder entdecken/benachrichtigen Sie beide!

Eine weitere (merkwürdige?) Beobachtung: Es gibt keine Anzeichen für ein First Responder Management in diesem Code. Ich habe dies bisher nur mit Table View Controllern ausprobiert und alles scheint sehr gut zusammen zu funktionieren! Für andere Szenarien kann ich mich allerdings nicht verbürgen.

Kendall, et. al - kann jemand sagen, warum das so sein könnte für UIWindow Unterklassen? Liegt es daran, dass das Fenster an der Spitze der Nahrungskette steht?

35voto

Ich stieß auf diesen Beitrag auf der Suche nach einer "Schüttel"-Implementierung. millenomis Antwort funktionierte gut für mich, obwohl ich nach etwas suchte, das ein bisschen mehr "Schüttel-Aktion" zum Auslösen benötigt. Ich habe den booleschen Wert durch einen int shakeCount ersetzt. Außerdem habe ich die Methode L0AccelerationIsShaking() in Objective-C neu implementiert. Sie können die Menge des erforderlichen Schüttelns anpassen, indem Sie den zu shakeCount hinzugefügten Wert verändern. Ich bin mir nicht sicher, ob ich schon die optimalen Werte gefunden habe, aber bisher scheint es gut zu funktionieren. Hoffentlich hilft das jemandem:

- (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: Ich habe das Aktualisierungsintervall auf 1/15-Sekunde eingestellt.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X