Frage

Ich möchte reagieren, wenn jemand das iPhone schüttelt. Es ist mir besonders egal, wie sie es schütteln, nur dass es für einen Bruchteil einer Sekunde kräftig geflacht wurde. Weiß jemand, wie man das erkennt?

War es hilfreich?

Lösung

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

Der Haupttrick besteht darin, dass Sie über eine UIView (nicht uiViewController) verfügen, die Sie als Firstresponder möchten, um die Shake -Ereignismeldungen zu erhalten. 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 Systemansichten) einfach in eine Ansicht umwandeln, mit der das Shake -Ereignis einfach durch Unterklagen der Ansicht mit nur diesen Methoden (und dann diesen neuen Typ anstelle des Basistyps in IB ausgewählt werden kann oder bei der Zuweisung eines verwendet wird Aussicht).

Im View Controller möchten Sie diese Ansicht so festlegen, dass er zu Ersthelfer wird:

- (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 von Benutzeraktionen (wie einem Suchleisten- oder Texteintragsfeld) zu Ersthelfer werden, auch den Schütteln zur Ersthelfer -Ansicht wiederherstellen müssen, wenn die andere Ansicht zurücktritt!

Diese Methode funktioniert auch, wenn Sie Applications -SupportSshaketoEdit auf NO festlegen.

Andere Tipps

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 Histeresis verhindert, dass das Shake -Ereignis mehrmals ausgelöst wird, bis der Benutzer den Shake stoppt.

Ich habe es schließlich mit Codebeispielen davon funktioniert Tutorial für das Tutorial für Wiederherstellung/Redo -Manager.
Genau das müssen Sie tun:

  • Setzen Sie die ApplicationsupportSshaketoedit Eigenschaft im Delegierten der App:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • Hinzufügen/überschreiben CanbecomeFirstresponder, ViewDidAppear: 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 beantragt Methode zu Ihrem Ansichtsregler:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    

    Erstens ist Kendalls Antwort am 10. Juli genau richtig.

    Jetzt ... wollte ich etwas Ähnliches machen (in iPhone OS 3.0+), nur in meinem Fall wollte ich es App-Wide, damit ich alarmieren konnte verschiedene Teile der App, wenn ein Shake auftrat. Folgendes habe ich am Ende gemacht.

    Zuerst habe ich unterklassifiziert UiWindow. Das ist einfach Peasy. Erstellen Sie eine neue Klassendatei mit einer Schnittstelle wie z. B. MotionWindow : UIWindow (Fühlen Sie sich frei, Ihre eigene zu wählen, Natch). Fügen Sie eine Methode wie SO hinzu:

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

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

    Wenn Sie nun einen MainWindow.xib (stock xcode template -Material) verwenden, gehen Sie dort hinein und ändern Sie die Klasse Ihres Fensterobjekts von UiWindow zu BewegungWindow oder was auch immer du es genannt hast. Speichern Sie den XIB. Wenn Sie einrichten UiWindow Verwenden Sie programmgesteuert stattdessen Ihre neue Fensterklasse.

    Jetzt verwendet Ihre App die Spezialisierung UiWindow Klasse. Wo immer Sie über einen Shake erzählt werden möchten, melden Sie sich für sie Benachrichtigungen an! So was:

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

    Sich als Beobachter zu entfernen:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    Ich stecke meine hinein viewwillappear: und Viewwilldisappear: wo Ansichtscontroller betroffen sind. Stellen Sie sicher, dass Ihre Antwort auf das Shake -Ereignis weiß, ob es "bereits in Arbeit" ist oder nicht. Andernfalls haben Sie, wenn das Gerät zweimal nacheinander geschüttelt wird, einen Li'l -Stau. Auf diese Weise können Sie andere Benachrichtigungen ignorieren, bis Sie wirklich fertig sind, um auf die ursprüngliche Benachrichtigung zu reagieren.

    Außerdem: Sie können sich dafür entscheiden, auszurichten MotionBegan vs. beantragt. Es liegt an dir. In meinem Fall muss der Effekt immer stattfinden nach Das Gerät ist in Ruhe (gegenüber dem Zittern), also benutze ich beantragt. Versuchen Sie es beides und sehen Sie, welches mehr Sinn macht ... oder erkennen/melden Sie für beide!

    Eine weitere (neugierige?) Beobachtung hier: Beachten Sie, dass es in diesem Code kein Anzeichen für das Ersthelfermanagement gibt. Ich habe dies nur mit Tabellenansichts -Controllern versucht und alles scheint ziemlich gut zusammen zu funktionieren! Ich kann jedoch nicht für andere Szenarien bürgen.

    Kendall, et. Al - Kann jemand darüber sprechen, warum dies so sein könnte? UiWindow Unterklassen? Liegt es daran, dass sich das Fenster oben in der Nahrungskette befindet?

    Ich bin auf diesen Beitrag gestoßen, der nach einer "Schütteln" implementiert war. Millenomis Antwort funktionierte gut für mich, obwohl ich nach etwas suchte, das ein bisschen mehr "schüttelnde Action" erforderte, um sie auszulösen. Ich habe durch einen Int ShakeCount durch einen Booleschen Wert ersetzt. Ich habe auch die L0accelerationSisshaking () -Methode in Objective-C neu implementiert. Sie können den Munmount des Schüttelns optimieren, der den Munmount zu Shakecount optimiert hat. Ich bin mir nicht sicher, ob ich die optimalen Werte noch 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 Update -Intervall auf 1/15 der Sekunde festgelegt.

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

    Sie müssen das Beschleunigungsmesser über den Beschleunigungsmesser überprüfen: Didaccelerate: Methode, das Teil des UIACCelerometerdelegate -Protokolls ist, und prüfen, ob die Werte über einen Schwellenwert für die für einen Shake benötigte Bewegung gehen.

    Im Beschleunigungsmesser befindet sich ein ordentlicher Beispielcode: Didaccelerate: Methode am Ende des AppController.m im GLPAINT -Beispiel, das auf der iPhone -Entwickler -Site verfügbar ist.

    In iOS 8.3 (vielleicht früher) mit Swift ist es so einfach wie das Überschreiben der motionBegan oder motionEnded Methoden in Ihrem View Controller:

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

    Dies ist der grundlegende Delegiertecode, 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];   
        }
    

    Legen Sie auch den entsprechenden Code in der Schnittstelle fest. dh:

    @Interface MyViewController: UIViewControlleru003CUIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate>

    Fügen Sie die folgenden Methoden in 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 
         }
    

    Es tut mir leid, dies eher als Antwort als als Kommentar zu veröffentlichen, aber wie Sie sehen können, bin ich neu, um Überlauf zu stapeln, und daher bin ich noch nicht seriös genug, um Kommentare zu veröffentlichen!

    Wie auch immer, ich zweite @cire, um sicherzustellen, dass der Ersthelferstatus festgelegt wird, sobald die Ansicht Teil der View -Hierarchie ist. Setzen Sie also den Ersthelferstatus in Ihren Ansichtscontrollern ein viewDidLoad Die Methode funktioniert zum Beispiel nicht. Und wenn Sie sich nicht sicher sind, ob es funktioniert [view becomeFirstResponder] Gibt Ihnen einen Booleschen zurück, den Sie testen können.

    Ein weiterer Punkt: du kann Verwenden Sie einen View Controller, um das Shake -Ereignis zu erfassen, wenn Sie nicht unnötig eine UIView -Unterklasse erstellen möchten. Ich weiß, es ist nicht so viel Ärger, aber die Option ist immer noch da. Bewegen Sie einfach die Code -Snippets, die Kendall in die UIView -Unterklasse in Ihren Controller gesteckt hat, und senden Sie die becomeFirstResponder und resignFirstResponder Nachrichten an self anstelle der Uiview -Unterklasse.

    Zunächst einmal weiß ich, dass dies ein alter Beitrag ist, aber es ist immer noch relevant, und ich fand heraus, dass die beiden höchsten Antworten nicht getan wurden Erkennen Sie den Shake so früh wie möglich. So geht es:

    1. Link Coremotion mit Ihrem Projekt in den Build -Phasen des Ziels:CoreMotion
    2. In Ihrem ViewController:

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

    Die einfachste Lösung besteht darin, ein neues Root -Fenster 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 Ihrem Bewerbungsdelegierter:

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

    Wenn Sie ein Storyboard verwenden, kann dies schwieriger sein. Ich kenne den Code, den Sie in der Anwendungsdelegierung benötigen, nicht genau.

    Verwenden Sie einfach diese drei Methoden dafür

    - (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 überprüfen dort

    Eine Swiftase -Version basiert auf der ersten Antwort!

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

    Um diese App-Wide zu aktivieren, habe ich eine Kategorie auf UIWindow erstellt:

    @implementation UIWindow (Utils)
    
    - (BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Do whatever you want here...
        }
    }
    
    @end
    
    Lizenziert unter: CC-BY-SA mit Zuschreibung
    Nicht verbunden mit StackOverflow
    scroll top