Question

Je veux réagir lorsque quelqu'un secoue l'iPhone. Je ne me soucie pas particulièrement de la façon dont ils secouent la situation, juste du fait qu’il a été agité vigoureusement pendant une fraction de seconde. Est-ce que quelqu'un sait comment détecter cela?

Était-ce utile?

La solution

Dans la version 3.0, il existe désormais un moyen plus simple de se connecter aux nouveaux événements de mouvement.

Le truc principal est que vous devez avoir UIView (et non UIViewController) comme premier répondeur pour recevoir les messages d’événement shake. Voici le code que vous pouvez utiliser dans n'importe quel UIView pour obtenir des événements shake:

@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

Vous pouvez facilement transformer n'importe quelle vue UIView (même les vues système) en une vue pouvant obtenir l'événement shake simplement en sous-classant la vue avec uniquement ces méthodes (puis en sélectionnant ce nouveau type à la place du type de base dans IB, ou en l'utilisant). lors de l'attribution d'une vue).

Dans le contrôleur de vue, vous souhaitez que cette vue devienne le premier répondeur:

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

N'oubliez pas que si d'autres vues devenant le premier intervenant à partir d'actions de l'utilisateur (comme une barre de recherche ou un champ de saisie de texte), vous devez également restaurer le statut de premier répondant qui bouge lorsque l'autre vue quitte.

Cette méthode fonctionne même si vous définissez applicationSupportsShakeToEdit sur NO.

Autres conseils

À partir de mon application 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

L'histérèse empêche le déclenchement de l'agitation de se déclencher plusieurs fois jusqu'à ce que l'utilisateur arrête l'agitation.

J'ai finalement réussi à le faire fonctionner en utilisant des exemples de code de cette Didacticiel d'annulation / rétablissement de gestionnaire .
C’est exactement ce que vous devez faire:

  • Définissez la propriété applicationSupportsShakeToEdit dans le délégué de l'application:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • Ajouter / ignorer canBecomeFirstResponder , viewDidAppear: et < i> viewWillDisappear: méthodes dans votre contrôleur de vue:
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    

  • Ajoutez la méthode motionEnded à votre contrôleur View:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    

    Tout d'abord, la réponse de Kendall du 10 juillet est parfaite.

    Maintenant ... je voulais faire quelque chose de similaire (dans iPhone OS 3.0+), mais seulement dans mon cas, je le voulais au niveau de l'application pour pouvoir alerter diverses parties de l'application lorsqu'un tremblement se produisait. eu lieu. Voici ce que j'ai fini par faire.

    Tout d'abord, j'ai sous-classé UIWindow . C'est facile à faire. Créez un nouveau fichier de classe avec une interface telle que MotionWindow : UIWindow (n'hésitez pas à choisir le vôtre, natch). Ajoutez une méthode comme celle-ci:

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

    Remplacez @"DeviceShaken" le nom de notification de votre choix. Enregistrez le fichier.

    Maintenant, si vous utilisez un fichier MainWindow.xib (modèle stocké dans le code Xcode), entrez-y et modifiez la classe de votre objet Window de UIWindow à MotionWindow , ou autre chose. vous l'avez appelé. Enregistrez le xib. Si vous configurez UIWindow par programme, utilisez plutôt votre nouvelle classe Window.

    Votre application utilise maintenant la classe spécialisée UIWindow . Où que vous vouliez être informé d'un shake, inscrivez-vous pour recevoir des notifications! Comme ceci:

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

    Pour vous retirer en tant qu'observateur:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    J'ai mis les miens dans viewWillAppear: et viewWillDisappear: en ce qui concerne les contrôleurs de vue. Assurez-vous que votre réponse à l'événement shake sait s'il est déjà en cours & & Quot; ou pas. Sinon, si le périphérique est secoué deux fois de suite, vous aurez un embouteillage léger. De cette façon, vous pouvez ignorer les autres notifications jusqu'à ce que vous ayez réellement répondu à la notification d'origine.

    En outre: vous pouvez choisir de vous séparer de motionBegan et de motionEnded . C'est à vous. Dans mon cas, l'effet doit toujours avoir lieu après que le périphérique soit au repos (par opposition au moment où il commence à trembler), j'utilise donc motionEnded . Essayez les deux et voyez lequel est le plus logique ... ou détectez / notifiez pour les deux!

    Encore une (curieuse?) observation ici: Notez que ce code ne montre aucun signe de gestion des premiers répondants. Je n’ai essayé jusqu’à présent avec les contrôleurs de vue de table et tout semble bien fonctionner ensemble! Je ne peux cependant pas garantir d'autres scénarios.

    Kendall, et. al - est-ce que quelqu'un peut expliquer pourquoi cela pourrait en être ainsi pour les sous-classes UIWindow ? Est-ce parce que la fenêtre se situe au sommet de la chaîne alimentaire?

    Je suis tombé sur ce message à la recherche d'un & "trembler &"; la mise en oeuvre. La réponse de millenomi a bien fonctionné pour moi, même si je cherchais quelque chose qui nécessitait un peu plus & "Action tremblante &"; déclencher. J'ai remplacé la valeur booléenne par un int shakeCount. J'ai également réimplémenté la méthode L0AccelerationIsShaking () dans Objective-C. Vous pouvez modifier la quantité de secousses requise en modifiant la quantité ajoutée à shakeCount. Je ne suis pas sûr d'avoir trouvé les valeurs optimales pour le moment, mais cela semble bien fonctionner jusqu'à présent. J'espère que cela aide quelqu'un:

    - (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: J'ai défini l'intervalle de mise à jour sur 1/15 de seconde.

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

    Vous devez vérifier l'accéléromètre via l'accéléromètre: didAccelerate: méthode qui fait partie du protocole UIAccelerometerDelegate et vérifier si les valeurs dépassent un seuil pour la quantité de mouvement nécessaire pour un tremblement.

    Il existe un exemple de code correct dans l'accéléromètre: didAccelerate: dans le fichier exemple de GLPaint, situé au bas de AppController.m, disponible sur le site du développeur iPhone.

    Dans iOS 8.3 (peut-être plus tôt précédemment) avec Swift, il suffit de remplacer les méthodes motionBegan ou motionEnded dans votre contrôleur de vue:

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

    Il s'agit du code de délégué de base dont vous avez besoin:

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

    Définissez également le dans le code approprié dans l'interface. c'est-à-dire:

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

    Ajouter les méthodes suivantes dans le fichier ViewController.m, son fonctionnement correct

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

    Désolé de poster cela comme une réponse plutôt que comme un commentaire, mais comme vous pouvez le constater, je suis un nouveau venu dans Stack Overflow et je ne suis donc pas encore assez réputé pour poster des commentaires!

    Quoi qu’il en soit, je pense à @cire de bien définir le statut de premier répondant une fois que la vue fait partie de la hiérarchie des vues. Par conséquent, la définition du statut de premier répondant dans votre méthode de contrôleur de vue viewDidLoad ne fonctionnera pas, par exemple. Et si vous n'êtes pas sûr de son fonctionnement, [view becomeFirstResponder] vous renvoie un booléen que vous pouvez tester.

    Autre point: vous pouvez utiliser un contrôleur de vue pour capturer l'événement shake si vous ne souhaitez pas créer inutilement une sous-classe UIView. Je sais que ce n'est pas si compliqué, mais l'option est toujours là. Déplacez simplement les extraits de code que Kendall a placés dans la sous-classe UIView dans votre contrôleur et envoyez les messages becomeFirstResponder et resignFirstResponder à self au lieu de la sous-classe UIView.

    Tout d’abord, je sais qu’il s’agit d’un ancien message, mais il reste pertinent et j’ai constaté que les deux réponses les plus votées n’ont pas détecté le tremblement le plus tôt possible . Voici comment faire:

    1. Liez CoreMotion à votre projet lors des phases de construction de la cible: CoreMotion
    2. Dans votre ViewController:

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

    La solution la plus simple consiste à créer une nouvelle fenêtre racine pour votre application:

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

    Ensuite, dans votre délégué d'application:

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

    Si vous utilisez un Storyboard, cela peut être plus compliqué, je ne & # 8217; je ne connais pas précisément le code dont vous aurez besoin dans le délégué à l'application.

    Utilisez simplement ces trois méthodes pour le faire

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

    pour plus de détails, vous pouvez consulter un exemple complet de code sur il

    Une version rapide basée sur la toute première réponse!

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

    Pour activer cette application, j'ai créé une catégorie dans UIWindow:

    @implementation UIWindow (Utils)
    
    - (BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Do whatever you want here...
        }
    }
    
    @end
    
    Licencié sous: CC-BY-SA avec attribution
    Non affilié à StackOverflow
    scroll top