2013-10-15 8 views
9

Sto lavorando al codice per una vista vassoio espandibile che utilizza UIDynamicAnimator per ottenere una bella animazione di espansione/contratto.UIDynamicAnimator rifiuta di raggiungere l'equilibrio quando è attivo un UIGravityBehavior

Per ottenere un'accelerazione realistica, utilizzo UIGravityBehavior per far cadere il vassoio, finché la "scheda" del vassoio non tocca la parte inferiore dello schermo.

Questo funziona bene, ma anche se tutti gli elementi nella scena si sono fermati, UIDynamicAnimatorDelegate dynamicAnimatorDidPause: non viene mai chiamato. Ciò significa che l'animatore continua a utilizzare i cicli della CPU per animare la scena (il delegato è impostato e viene attivato per UIDynamicAnimatorDelegate dynamicAnimatorDidPause:).

Ho provato a rimuovere lo UIGravityBehavior dalla scena, causando effettivamente l'arresto dell'animatore alla fine. Non riesco a tempo a rimediare al comportamento gravitazionale, dato che ho bisogno di rimuoverlo dalla scena una volta che tutto ha smesso di muoversi.

Capisco che la forza di gravità è una forza costante, ma ho sempre pensato che avrebbe fermato l'animatore una volta che tutto ha 0 velocità e 0 accelerazione.

Questa ultima ipotesi è falsa?

Chiunque abbia problemi simili?

+0

Per chiunque stia leggendo questo: potrei aver trovato la soluzione. Non l'ho ancora provato, ma mi sono imbattuto nello stesso problema in una situazione diversa: stavo usando UIDynamicAnimator updateItemUsingCurrentState all'interno di un blocco di animazione. Questo non era il caso nella mia classe TrayView, ma ho pensato che questa realizzazione potrebbe aiutare le persone in futuro. Se avrò tempo cercherò di ispezionare il mio utilizzo di updateItemUsingCurrentState e vedere se riesco a impedire che il problema si verifichi nella mia classe TrayView. – Nailer

risposta

6

È corretto che l'animatore debba sospendere una volta che tutto è a riposo.

Controllare quali items sono collegati al comportamento gravitazionale e assicurarsi che non ci siano altri elementi ancora in caduta. Ad esempio, è facile creare accidentalmente il seguente errore:

  • Aggiungi al fine di gravità e di collisione
  • Rimuovere vista dalla superview e dalla collisione
  • non riescono a rimuovere vista dalla gravità

In questa situazione, la "voce fantasma" cadrà per sempre.

Un altro possibile problema (sebbene meno probabile dato la tua descrizione) è se i tuoi articoli sono collegati ad altri comportamenti che causano infiniti ma piccoli "rimbalzi". Vorrei controllare l'elenco completo dei comportamenti sul tuo animatore (ricordati di controllare anche i comportamenti dei bambini). In particolare sarei interessato a qualsiasi UIDynamicItemBehavior che aggiunge elasticity.


EDIT:

Potreste anche voler andare nella direzione opposta. Inizia con un sistema di dinamiche molto semplice e aggiungi componenti dalla tua finché non riesci a riprodurre il problema.Per esempio, il seguente non convergere abbastanza rapidamente (logging "pausa"):

@interface PTLViewController() <UIDynamicAnimatorDelegate> 
@property (nonatomic, strong) UIDynamicAnimator *animator; 
@end 

@implementation PTLViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100,100,100,100)]; 
    view.backgroundColor = [UIColor lightGrayColor]; 
    [self.view addSubview:view]; 

    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
    self.animator.delegate = self; 

    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[view]]; 
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES; 
    [self.animator addBehavior:collisionBehavior]; 

    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[view]]; 
    [self.animator addBehavior:gravityBehavior]; 
} 

- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator { 
    NSLog(@"pause"); 
} 
@end 

Alla tua domanda su come ottenere tutte le velocità voce, non so di un modo semplice per farlo. Purtroppo, UIDynamicAnimator non conosce direttamente tutti i suoi articoli. Questo è indiretto perché UIDyanamicBehavior non include una proprietà items. Se questo ti infastidisce quanto me, considera l'ingannare radar://15054405.

Ma c'è una soluzione se si desidera solo conoscere la velocità lineare attuale di elementi specifici. Basta aggiungere un UIDynamicItemBehavior con un'azione personalizzata per accedere esso:

UIDynamicItemBehavior *dynamicItemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[view]]; 
__weak UIDynamicItemBehavior *weakBehavior = dynamicItemBehavior; 
dynamicItemBehavior.action = ^{ 
    NSLog(@"Velocity: %@", NSStringFromCGPoint([weakBehavior linearVelocityForItem:view])); 
}; 
[self.animator addBehavior:dynamicItemBehavior]; 
+0

Sospetto anche il rimbalzo infinitamente piccolo di cui parli. Ho provato a rimuovere qualsiasi elemento con elasticità. È possibile stampare in qualche modo la velocità/accelerazione degli oggetti nell'animatore? – Nailer

+0

Grazie per la tua modifica dettagliata. Ritornerò sicuramente sul vassoio rimbalzante in futuro. Al momento l'ho appena sostituito con la mia vecchia implementazione pre-UIDynamics. I vassoi gonfiabili non sono i più alti nella mia lista di priorità in questo momento;) Accetterò comunque la tua risposta. – Nailer

1

Ho avuto un problema simile di recente. Alla fine ho usato un UICollisionBehavior con i confini invece di oggetti (perché altrimenti gli oggetti in movimento sono stati urtano gli altri ...) e implementare il metodo delegato collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint: sapere quando dovrei togliere la gravità

UICollisionBehavior *collide = [[[UICollisionBehavior alloc] initWithItems:borders] retain]; 
[collide addItem:movingItem]; 

[collide setCollisionMode:UICollisionBehaviorModeBoundaries]; 
[collide setTranslatesReferenceBoundsIntoBoundary:YES]; 

Se trovate una soluzione migliore, fammi sapere :)!

+0

Sto usando i limiti per mantenere il mio vassoio in posizione. Il problema con l'aggiunta di qualcosa in beginContactForItem è che il mio vassoio rimbalza una volta raggiunto il fondo dello schermo. Quindi il primo rimbalzo disattiverà la gravità, e non colpirà mai il fondo in quanto rimbalzerà sullo zero gravità. – Nailer

+0

E se rimuovi completamente gli elementi dall'animatore quando toccano il fondo per terminare l'animazione da solo? O se hai un solo oggetto, forse puoi sostituirlo correttamente con un'animazione quando viene attivato 'dynamicAnimatorDidPause:' – KIDdAe

+0

'dynamicAnimatorDidPause:' Non viene mai attivato, ma una soluzione potrebbe essere quella di terminare l'animazione manualmente dopo la prima collisione. – Nailer

1

Il mio problema è lo stesso. Il mio animatore non si ferma mai quindi una volta avviato, la mia app consuma per sempre una CPU compresa tra il 3 e il 4%. Tutti i miei punti di vista sembrano smettere di muoversi entro 1/2 secondo. Quindi, piuttosto che capire perché non raggiungo l'equilibrio, lo colpisco con un martello e uccido l'animatore con un timer. Gli do 2 secondi.

- (void)createAnimator { 
    if (_timer) { 
     [_timer invalidate]; 
    } 

    if (_animator) { 
     [_animator removeAllBehaviors]; 
    } 

    _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 

    // create all behaviors 

    // kill the animator in 2 seconds 
    _timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(killAnimator:) userInfo:nil repeats:NO]; 
} 

- (void)killAnimator:(NSTimer *)timer { 
    [_animator removeAllBehaviors]; 
    _animator = nil; 
    _timer = nil; 
}