it-swarm.com.de

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

Ich möchte reagieren, wenn jemand das iPhone schüttelt. Es ist mir egal, wie sie es schütteln, nur dass es für den Bruchteil einer Sekunde heftig geschwenkt wurde. Weiß jemand, wie man das erkennt?

339
Josh Gagnon

In 3.0 gibt es jetzt einen einfacheren Weg - haken Sie in die neuen Bewegungsereignisse ein.

Der Haupttrick besteht darin, dass Sie über UIView (nicht UIViewController) verfügen müssen, das Sie als firstResponder verwenden möchten, um die Shake-Ereignismeldungen zu empfangen. Hier ist der Code, den Sie in jedem UIView verwenden können, um Shake-Ereignisse abzurufen:

@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 (auch Systemansichten) auf einfache Weise in eine Ansicht umwandeln, die das Verwacklungsereignis erhalten kann, indem Sie die Ansicht nur mit diesen Methoden in Unterklassen unterteilen (und dann diesen neuen Typ anstelle des Basistyps in IB auswählen oder ihn beim Zuweisen von a verwenden Aussicht).

In der Ansichtssteuerung möchten Sie diese Ansicht als Ersthelfer festlegen:

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

Vergessen Sie nicht, dass Sie bei anderen Ansichten, die als Ersthelfer von Benutzeraktionen fungieren (z. B. eine Suchleiste oder ein Texteingabefeld), auch den Ersthelferstatus der Schüttelansicht wiederherstellen müssen, wenn die andere Ansicht zurücktritt!

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

Aus meiner 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 Histeresis verhindert, dass das Erschütterungsereignis mehrmals ausgelöst wird, bis der Benutzer die Erschütterung beendet.

179
millenomi

Ich habe es endlich geschafft, indem ich Codebeispiele aus diesem ndo/Redo Manager Tutorial verwendet habe.
Genau das müssen Sie tun:

  • Stellen Sie die applicationSupportsShakeToEdit Eigenschaft im Delegierten der App:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    
  • Hinzufügen/Überschreiben canBecomeFirstResponderviewDidAppear: und 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];
    }
    
  • Ergänzen Sie die motionEnded Methode zu Ihrem View Controller:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    
    154
    Eran Talmor

    Erstens ist Kendalls Antwort vom 10. Juli genau richtig.

    Nun ... ich wollte etwas Ähnliches machen (in iPhone OS 3.0+), nur in meinem Fall wollte ich es app-weit, damit ich verschiedene alarmieren konnte Teile der App, als ein Shake auftrat. Folgendes habe ich letztendlich getan.

    Zuerst habe ich IWindow untergeordnet. Das ist einfach peasy. Erstellen Sie eine neue Klassendatei mit einer Schnittstelle wie MotionWindow : UIWindow (zögern Sie nicht, Ihre eigene zu wählen, nicken). 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];
        }
    }
    

    Veränderung @"DeviceShaken" auf den Benachrichtigungsnamen Ihrer Wahl. Speicher die Datei.

    Wenn Sie nun eine MainWindow.xib (Standard-Xcode-Vorlage) verwenden, gehen Sie dort hinein und ändern Sie die Klasse Ihres Window-Objekts von IWindow in MotionWindow oder wie auch immer Sie es genannt haben . Speichern Sie die Xib. Wenn Sie IWindow programmgesteuert einrichten, verwenden Sie stattdessen Ihre neue Window-Klasse.

    Jetzt verwendet Ihre App die spezialisierte IWindow -Klasse. Wo immer Sie über einen Shake informiert werden möchten, melden Sie sich für Benachrichtigungen an! So was:

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

    So entfernen Sie sich als Beobachter:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    Ich habe meine in viewWillAppear: und viewWillDisappear: eingetragen, wenn es um View-Controller geht. Stellen Sie sicher, dass Ihre Antwort auf das Verwacklungsereignis weiß, ob es "bereits in Bearbeitung" ist oder nicht. Andernfalls kommt es zu einem Stau, wenn das Gerät zweimal hintereinander geschüttelt wird. Auf diese Weise können Sie andere Benachrichtigungen ignorieren, bis Sie die ursprüngliche Benachrichtigung vollständig beantwortet haben.

    Außerdem: Sie können wählen, ob motionBegan vs. motionEnded abgehakt werden soll. Es liegt an dir. In meinem Fall muss der Effekt immer stattfinden , nachdem das Gerät in Ruhe ist (im Vergleich zu dem Zeitpunkt, an dem es zu zittern beginnt), also verwende ich motionEnded. Probieren Sie beides aus und finden Sie heraus, welches sinnvoller ist ... oder erkennen/benachrichtigen Sie beide!

    Noch eine (neugierige?) Beobachtung hier: Beachten Sie, dass in diesem Code keine Anzeichen für ein Ersthelfer-Management vorhanden sind. Ich habe dies bisher nur mit Table View Controllern versucht und alles scheint ganz gut zusammen zu funktionieren! Für andere Szenarien kann ich mich allerdings nicht verbürgen.

    Kendall et al. al - kann jemand sagen, warum dies für IWindow Unterklassen so sein könnte? Liegt es daran, dass sich das Fenster oben in der Nahrungskette befindet?

    94
    Joe D'Andrea

    Ich bin auf diesen Beitrag gestoßen und habe nach einer "erschütternden" Implementierung gesucht. Die Antwort von millenomi funktionierte gut für mich, obwohl ich nach etwas suchte, das etwas mehr "Rütteln" zum Auslösen erforderte. Ich habe den Booleschen Wert durch einen int shakeCount ersetzt. Ich habe auch die L0AccelerationIsShaking () -Methode in Objective-C neu implementiert. Sie können die erforderliche Menge an Schütteln optimieren, indem Sie die Menge, die zu shakeCount hinzugefügt wurde, optimieren. Ich bin nicht sicher, ob ich die optimalen Werte gefunden habe, aber es scheint bisher gut zu funktionieren. Hoffe das hilft 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)];
    
    34
    Jeremy Ellison

    Sie müssen den Beschleunigungsmesser über die Methode "accelerometer: didAccelerate:" überprüfen, die Teil des Protokolls "UIAccelerometerDelegate" ist, und prüfen, ob die Werte einen Schwellenwert für die für ein Schütteln erforderliche Bewegungsmenge überschreiten.

    Es gibt einen anständigen Beispielcode im Accelerometer: didAccelerate: -Methode ganz unten in AppController.m im GLPaint-Beispiel, das auf der iPhone-Entwicklerseite verfügbar ist.

    12
    Dave Verwer

    In iOS 8.3 (möglicherweise früher) mit Swift ist es so einfach, die Methoden motionBegan oder motionEnded in Ihrem View-Controller zu überschreiben:

    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!")
        }
    }
    
    12
    nhgrif

    Dies ist der grundlegende Delegatencode, den Sie benötigen:

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

    Stellen Sie auch den entsprechenden Code im Interface ein. d.h.

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

    9

    Fügen Sie die folgenden Methoden zur ViewController.m-Datei hinzu, die ordnungsgemäß funktioniert

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

    Es tut mir leid, dies als Antwort und nicht als Kommentar zu posten, aber wie Sie sehen können, bin ich neu bei Stack Overflow und daher noch nicht seriös genug, um Kommentare zu posten!

    Wie auch immer, ich möchte bei @cire sicherstellen, dass der First-Responder-Status festgelegt wird, sobald die Ansicht Teil der Ansichtshierarchie ist. Das Festlegen des Ersthelferstatus in der viewDidLoad -Methode Ihres View Controllers funktioniert beispielsweise nicht. Und wenn Sie sich nicht sicher sind, ob es funktioniert [view becomeFirstResponder] gibt einen Booleschen Wert zurück, den Sie testen können.

    Ein weiterer Punkt: Sie können verwenden einen View-Controller, um das Shake-Ereignis zu erfassen, wenn Sie nicht unnötigerweise eine UIView-Unterklasse erstellen möchten. Ich weiß, es ist nicht so viel Aufwand, aber die Option ist immer noch da. Verschieben Sie einfach die Codeausschnitte, die Kendall in die UIView-Unterklasse eingefügt hat, in Ihren Controller und senden Sie die Nachrichten becomeFirstResponder und resignFirstResponder an self anstelle der UIView-Unterklasse.

    5
    Newtz

    Zunächst einmal weiß ich, dass dies ein alter Beitrag ist, aber er ist immer noch relevant, und ich stellte fest, dass die beiden Antworten mit den höchsten Stimmen nicht zutrafen Erkennen Sie die Erschütterung so früh wie möglich. So geht's:

    1. Verknüpfen Sie CoreMotion in den Erstellungsphasen des Ziels mit Ihrem Projekt: CoreMotion
    2. In Ihrem ViewController:

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

    Die einfachste Lösung besteht darin, ein neues Stammfenster für Ihre Anwendung abzuleiten:

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

    Dann in Ihrer Anwendung delegieren:

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

    Wenn Sie ein Storyboard verwenden, ist dies möglicherweise schwieriger. Ich kenne den Code, den Sie für den Anwendungsdelegierten benötigen, nicht genau.

    4
    mxcl

    Eine Swiftease-Version basierend auf der allerersten Antwort!

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

    Verwenden Sie dazu einfach diese drei Methoden

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

    für Details können Sie einen vollständigen Beispielcode über dort überprüfen

    1
    Mashhadi