31

Vorrei poter spostare una sottoview sullo schermo e fuori dallo schermo molto simile a come si naviga tra le immagini nell'app per iPhone nell'app Foto, quindi se la sottoview è più di 1/2 di schermo disattivato quando lo lascio andare con il dito deve animare fuori dallo schermo, ma deve anche supportare lo scorrimento, quindi se la velocità di scorrimento/panoramica è sufficientemente alta deve animare lo schermo anche se potrebbe essere inferiore a 1/2 dello schermo.Animazione UIView basata su velocità UIPanGestureRecognizer

La mia idea era di usare UIPanGestureRecognizer e quindi testare la velocità. Funziona, ma come faccio a impostare una durata dell'animazione corretta per lo spostamento di UIView in base alla posizione corrente della vista e alla velocità della panoramica in modo che appaia uniforme? Se imposto un valore fisso, l'animazione inizia a rallentare o a rallentare rispetto alla velocità di scorrimento delle dita.

risposta

47

I documenti dicono

La velocità del gesto vaschetta, che è espressa in punti per secondo. La velocità è suddivisa in componenti orizzontali e verticali.

Quindi direi, dato che si desidera spostare la visualizzazione xPoints (misurata in pt) per lasciarlo andare fuori dallo schermo, è possibile calcolare la durata di quel movimento in questo modo:

CGFloat xPoints = 320.0; 
CGFloat velocityX = [panRecognizer velocityInView:aView].x; 
NSTimeInterval duration = xPoints/velocityX; 
CGPoint offScreenCenter = moveView.center; 
offScreenCenter.x += xPoints; 
[UIView animateWithDuration:duration animations:^{ 
    moveView.center = offScreenCenter; 
}]; 

Si potrebbe invece utilizzare + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion e provare diversi UIViewAnimationOptions.

20

Un'osservazione su come fare affidamento sulla velocità in UIPanGestureRecognizer: Non conosco la tua esperienza, ma ho trovato che la velocità generata dal simulatore del sistema non è terribilmente utile. (È ok sul dispositivo, ma problematico sul simulatore.)

Se si esegue rapidamente una panoramica e si interrompe bruscamente, attendere e solo dopo aver terminato il gesto (ad esempio, l'utente avvia uno scorrimento, si rende conto che non era quello che volevano , quindi si fermano e quindi rilasciano il dito), la velocità riportata da velocityInView: allo stato UIGestureRecognizerStateEnded quando il dito è stato rilasciato sembra essere la velocità veloce prima che mi fermassi e aspettassi, mentre la velocità giusta in questo esempio sarebbe zero (o vicino zero). In breve, la velocità riportata è quella che era giusto prima della fine della padella, ma non la velocità alla fine della padella stessa.

Alla fine ho calcolato manualmente la velocità. (Sembra sciocco che sia necessario, ma non ho visto alcun modo se volevo ottenere la velocità finale della padella.) In conclusione, quando lo stato è UIGestureRecognizerStateChanged tengo traccia delle attuali e precedenti translationInView CGPoint così come il tempo, e quindi utilizzando quei valori quando ero nello UIGestureRecognizerStateEnded per calcolare la velocità finale effettiva. Funziona piuttosto bene.

Ecco il mio codice per il calcolo della velocità. Mi è capitato di non usare la velocità per calcolare la velocità dell'animazione, ma piuttosto la sto usando per determinare se l'utente ha effettuato una panoramica sufficiente o ha effettuato uno sfarfallio abbastanza velocemente da consentire alla vista di spostarsi oltre la metà dello schermo e quindi innescando l'animazione tra le visualizzazioni, ma il concetto di calcolare la velocità finale sembra applicabile a questa domanda. Ecco il codice:

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture 
{ 
    static CGPoint lastTranslate; // the last value 
    static CGPoint prevTranslate; // the value before that one 
    static NSTimeInterval lastTime; 
    static NSTimeInterval prevTime; 

    CGPoint translate = [gesture translationInView:self.view]; 

    if (gesture.state == UIGestureRecognizerStateBegan) 
    { 
     lastTime = [NSDate timeIntervalSinceReferenceDate]; 
     lastTranslate = translate; 
     prevTime = lastTime; 
     prevTranslate = lastTranslate; 
    } 
    else if (gesture.state == UIGestureRecognizerStateChanged) 
    { 
     prevTime = lastTime; 
     prevTranslate = lastTranslate; 
     lastTime = [NSDate timeIntervalSinceReferenceDate]; 
     lastTranslate = translate; 

     [self moveSubviewsBy:translate]; 
    } 
    else if (gesture.state == UIGestureRecognizerStateEnded) 
    { 
     CGPoint swipeVelocity = CGPointZero; 

     NSTimeInterval seconds = [NSDate timeIntervalSinceReferenceDate] - prevTime; 
     if (seconds) 
     { 
      swipeVelocity = CGPointMake((translate.x - prevTranslate.x)/seconds, (translate.y - prevTranslate.y)/seconds); 
     } 

     float inertiaSeconds = 1.0; // let's calculate where that flick would take us this far in the future 
     CGPoint final = CGPointMake(translate.x + swipeVelocity.x * inertiaSeconds, translate.y + swipeVelocity.y * inertiaSeconds); 

     [self animateSubviewsUsing:final]; 
    } 
} 
+0

Non funziona in iOS 6 perché la traduzione finale e l'ultima traduzione è la stessa in "UIGestureRecognizerStateEnded", quindi la velocità finale è sempre 0. – an0

+1

Grazie, vedo. È un problema di simulatore. Ho lanciato un bug report ad Apple. – an0

+0

Ho modificato la fonte (cambiando alcuni nomi di variabili) per evitare di evitare questa confusione. Questo codice ha funzionato bene, ma si spera che i nuovi nomi delle variabili rendano persino un po 'meno confusionario. Grazie per aver segnalato il bug ad Apple. – Rob

1

Nella maggior parte dei casi l'impostazione UIViewAnimationOptionBeginFromCurrentState opzione per UIView animatore è sufficiente per fare un'animazione impeccabile continuato.

Problemi correlati