Domanda

Voglio reagire quando qualcuno scuote l'iPhone.Io non amo particolarmente la cura come si agita, solo che era agitata vigorosamente per una frazione di secondo.Qualcuno sa come individuare questo?

È stato utile?

Soluzione

In 3.0, ora c'è un modo più semplice gancio nel nuovo movimento di eventi.

Il trucco principale è che è necessario disporre di alcuni UIView (non UIViewController) che si desidera, come firstResponder per ricevere la scossa messaggi di evento.Ecco il codice che è possibile utilizzare in qualsiasi UIView avere del mosso eventi:

@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

Si può facilmente trasformare qualsiasi UIView (anche viste di sistema) in una vista che si può ottenere con il frullato di evento semplicemente da una sottoclasse della vista con questi metodi (e quindi selezionando questo nuovo tipo invece di base tipo IB, o usarlo quando l'allocazione di vista).

Nella view controller che si desidera impostare questo punto di vista diventare first responder:

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

Non dimenticate che se avete altre opinioni che diventano prima responder da azioni dell'utente (come una barra di ricerca o campo di immissione di testo) inoltre, è necessario ripristinare l'agitazione vista first responder stato quando la visualizzazione di altri si dimette!

Questo metodo funziona anche se si imposta applicationSupportsShakeToEdit di NO.

Altri suggerimenti

Dalla mia 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'isteresi impedisce all'evento di scossa di innescarsi più volte fino a quando l'utente non interrompe la scossa.

Alla fine l'ho fatto funzionare usando esempi di codice da questo Tutorial Annulla / Ripeti gestore .
Questo è esattamente ciò che devi fare:

  • Imposta la proprietà applicationSupportsShakeToEdit nel delegato dell'app:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • Aggiungi / Sostituisci canBecomeFirstResponder , viewDidAppear: e < i> viewWillDisappear: metodi nel View Controller:
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    

  • Aggiungi il metodo motionEnded al tuo controller di visualizzazione:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    

    Innanzitutto, la risposta del 10 luglio di Kendall è perfetta.

    Ora ... volevo fare qualcosa di simile (in iPhone OS 3.0+), solo nel mio caso lo volevo a livello di app in modo da poter avvisare varie parti dell'app quando si agita si è verificato. Ecco cosa ho finito per fare.

    Innanzitutto, ho eseguito la sottoclasse di UIWindow . Questo è facile. Crea un nuovo file di classe con un'interfaccia come MotionWindow : UIWindow (sentiti libero di scegliere il tuo, natch). Aggiungi un metodo in questo modo:

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

    Cambia @"DeviceShaken" con il nome della notifica che preferisci. Salva il file.

    Ora, se usi MainWindow.xib (materiale modello Xcode di scorta), entra e cambia la classe dell'oggetto Window da UIWindow a MotionWindow o qualsiasi altra cosa l'hai chiamato. Salva lo xib. Se hai impostato UIWindow a livello di codice, usa invece la tua nuova classe Window.

    Ora l'app utilizza la classe specializzata UIWindow . Ovunque tu voglia sentirti dire di una scossa, iscriviti per ricevere le notifiche! In questo modo:

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

    Per rimuovere te stesso come osservatore:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    Ho inserito il mio in viewWillAppear: e viewWillDisappear: per quanto riguarda i controller di visualizzazione. Assicurati che la tua risposta all'evento shake sappia se è & Quot; già in corso & Quot; o no. Altrimenti, se il dispositivo viene scosso due volte in successione, avrai un ingorgo. In questo modo puoi ignorare altre notifiche fino a quando non hai veramente risposto alla notifica originale.

    Inoltre: puoi scegliere di inserire motionBegan rispetto a motionEnded . Tocca a voi. Nel mio caso, l'effetto deve sempre avvenire dopo il dispositivo è a riposo (rispetto a quando inizia a tremare), quindi utilizzo motionEnded . Prova entrambi e vedi quale ha più senso ... o rileva / notifica per entrambi!

    Un'altra osservazione (curiosa?) qui: Notare che non ci sono segni di gestione del primo soccorritore in questo codice. Finora ci ho provato solo con i controller di visualizzazione tabella e tutto sembra funzionare abbastanza bene insieme! Non posso garantire altri scenari.

    Kendall, et. al - qualcuno può dire perché potrebbe essere così per le sottoclassi UIWindow ? È perché la finestra è in cima alla catena alimentare?

    Mi sono imbattuto in questo post in cerca di un " scuotendo " implementazione. La risposta di Millenomi ha funzionato bene per me, anche se stavo cercando qualcosa che richiedesse un po 'più di " agitazione dell'azione " innescare. Ho sostituito il valore booleano con un shakeCount int. Ho anche reimplementato il metodo L0AccelerationIsShaking () in Objective-C. È possibile modificare la quantità di agitazione richiesta modificando la quantità aggiunta a shakeCount. Non sono sicuro di aver ancora trovato i valori ottimali, ma sembra che stia funzionando bene finora. Spero che questo aiuti qualcuno:

    - (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: Ho impostato l'intervallo di aggiornamento su 1/15 di secondo.

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

    È necessario controllare l'accelerometro tramite accelerometro: didAccelerate: metodo che fa parte del protocollo UIAccelerometerDelegate e verificare se i valori superano una soglia per la quantità di movimento necessaria per uno shake.

    Esiste un codice di esempio decente nell'accelerometro: didAccelerate: metodo proprio nella parte inferiore di AppController.m nell'esempio GLPaint che è disponibile sul sito degli sviluppatori iPhone.

    In iOS 8.3 (forse in precedenza) con Swift, è semplice come sovrascrivere i metodi motionBegan o motionEnded nel controller della vista:

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

    Questo è il codice delegato di base necessario:

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

    Impostare anche il codice appropriato nell'interfaccia. cioè:

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

    Aggiungi i seguenti metodi nel file ViewController.m, funziona correttamente

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

    Mi dispiace pubblicare questo come una risposta piuttosto che come un commento, ma come puoi vedere sono nuovo di Stack Overflow e quindi non sono ancora abbastanza affidabile per pubblicare commenti!

    Comunque secondo @cire per essere sicuro di impostare lo stato del primo risponditore una volta che la vista fa parte della gerarchia della vista. Pertanto, ad esempio, l'impostazione dello stato del primo risponditore nel metodo viewDidLoad dei controller di visualizzazione non funziona. E se non sei sicuro che funzioni [view becomeFirstResponder] ti restituisce un valore booleano che puoi testare.

    Un altro punto: puoi utilizzare un controller di visualizzazione per acquisire l'evento shake se non desideri creare una sottoclasse UIView inutilmente. So che non è così tanto fastidio, ma l'opzione è ancora lì. Basta spostare i frammenti di codice che Kendall ha inserito nella sottoclasse UIView nel controller e inviare i messaggi becomeFirstResponder e resignFirstResponder a self anziché alla sottoclasse UIView.

    Prima di tutto, so che questo è un vecchio post, ma è ancora pertinente e ho scoperto che le due risposte con il voto più alto non hanno rilevato il tremolio il prima possibile . Ecco come farlo:

    1. Collega CoreMotion al tuo progetto nelle fasi di costruzione del target: CoreMotion
    2. In ViewController:

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

    La soluzione più semplice è derivare una nuova finestra di root per la tua applicazione:

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

    Quindi nel delegato dell'applicazione:

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

    Se stai usando uno Storyboard, questo potrebbe essere più complicato, non so & # 8217; t so esattamente il codice che ti servirà nel delegato dell'applicazione.

    Usa questi tre metodi per farlo

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

    per i dettagli è possibile controllare un codice di esempio completo su

    Una versione rapida basata sulla prima risposta!

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

    Per abilitare questa app, ho creato una categoria su UIWindow:

    @implementation UIWindow (Utils)
    
    - (BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Do whatever you want here...
        }
    }
    
    @end
    
    Autorizzato sotto: CC-BY-SA insieme a attribuzione
    Non affiliato a StackOverflow
    scroll top