2013-10-15 52 views
9

Ich arbeite an Code für eine erweiterbare Tray-Ansicht, die UIDynamicAnimator verwendet, um eine schöne Expand/Contract-Animation zu erzielen.UIDynamicAnimator verweigert den Ausgleich, wenn ein UIGravityBehavior aktiv ist

Um eine realistische Beschleunigung zu erreichen, verwende ich UIGravityBehavior, um mein Fach fallen zu lassen, bis der "Vorsprung" des Fachs den unteren Rand des Bildschirms trifft.

Das funktioniert gut, aber obwohl alle Elemente in der Szene nicht mehr bewegt werden, wird UIDynamicAnimatorDelegate dynamicAnimatorDidPause: nie aufgerufen. Dies bedeutet, dass der Animator weiterhin CPU-Zyklen verwendet, um die Szene zu animieren (der Delegat ist festgelegt und wird für UIDynamicAnimatorDelegate dynamicAnimatorDidPause: ausgelöst).

Ich habe versucht, die UIGravityBehavior aus der Szene entfernen, die in der Tat den Animator am Ende zu stoppen verursacht. Ich kann die Entfernung des Gravitationsverhaltens jedoch nicht zeitgesteuert durchführen, da ich es von der Szene entfernen muss, sobald sich alles nicht mehr bewegt.

Ich verstehe, dass die Schwerkraft eine konstante Kraft ist, aber ich nahm immer noch an, dass es den Animator stoppen würde, sobald alles 0 Geschwindigkeit und 0 Beschleunigung hat.

Ist diese letzte Annahme falsch?

Wer hat ähnliche Probleme?

+0

Jedem, der dies liest: Ich könnte die Lösung gefunden haben. Ich habe es noch nicht getestet, aber ich stieß auf das gleiche Problem in einer anderen Situation: Ich habe den UIDynamicAnimator updateItemUsingCurrentState in einem Animationsblock verwendet. Dies war in meiner TrayView-Klasse nicht der Fall, aber ich dachte mir, dass diese Erkenntnis den Menschen in Zukunft helfen könnte. Wenn ich Zeit habe, werde ich versuchen, meine Verwendung des updateItemUsingCurrentState zu überprüfen und zu sehen, ob ich das Problem in meiner TrayView-Klasse verhindern kann. – Nailer

Antwort

6

Sie haben Recht, dass der Animator pausieren sollte, sobald alles zur Ruhe gekommen ist.

Überprüfen Sie, was items Ihrem Schwerkraftverhalten zugeordnet sind, und stellen Sie sicher, dass keine anderen Objekte noch fallen. Zum Beispiel ist es leicht, die folgenden Fehler versehentlich zu erstellen:

  • hinzufügen Ansicht der Schwerkraft und Kollision
  • entfernen Ansicht von Superview und aus Kollision
  • Ausfallen Ansicht von der Schwerkraft entfernen

In dieser Situation wird das "Geisterobjekt" für immer fallen.

Ein anderes mögliches Problem (obwohl mit Ihrer Beschreibung weniger wahrscheinlich) ist, wenn Ihre Elemente an andere Verhaltensweisen angehängt sind, die unendliche, aber kleine "Bounce" verursachen. Ich würde die vollständige Liste der Verhaltensweisen auf Ihrem Animator überprüfen (denken Sie daran, auch das Verhalten von Kindern zu überprüfen). Insbesondere würde ich mich für alle UIDynamicItemBehavior interessieren, die elasticity hinzufügt.


EDIT:

Sie können auch in die andere Richtung zu gehen. Beginnen Sie mit einem sehr einfachen Dynamiksystem und fügen Sie Komponenten von Ihnen hinzu, bis Sie das Problem reproduzieren können.Zum Beispiel der folgende konvergiert sehr schnell (Protokollierung „Pause“):

@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 

Um Ihre Frage über alle Artikel Geschwindigkeiten bekommen, weiß ich nicht eine einfache Möglichkeit, das zu tun. Leider kennt UIDynamicAnimator nicht alle seine Einzelteile direkt. Dies ist indirekt, weil UIDyanamicBehavior keine items Eigenschaft enthält. Wenn dich das so sehr stört wie mich, dann denke über das Duplizieren nach: radar://15054405.

Aber es gibt eine Lösung, wenn Sie nur die aktuelle lineare Geschwindigkeit bestimmter Elemente wissen möchten. Fügen Sie einfach ein UIDynamicItemBehavior mit einer benutzerdefinierten Aktion es zu protokollieren:

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

Ich vermute auch den unendlich kleinen Bounce, den du erwähnst. Ich habe versucht, alle Elemente mit Elastizität zu entfernen. Ist es möglich, die Geschwindigkeit/Beschleunigung von Objekten im Animator irgendwie zu drucken? – Nailer

+0

Danke für Ihre detaillierte Bearbeitung. Ich werde das Hüpfbrett in Zukunft definitiv wieder besuchen. Derzeit habe ich es gerade durch meine alte Pre-UIDynamics-Implementierung ersetzt. Bouncys sind momentan nicht auf meiner Prioritätenliste;) Ich werde deine Antwort aber akzeptieren. – Nailer

1

Ich hatte vor kurzem ein ähnliches Problem. Am Ende habe ich eine UICollisionBehavior mit Grenzen anstelle von Elementen (weil sonst die beweglichen Elemente, die anderen wurden stoßen ...) und Umsetzung der Delegatmethode collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint: wissen, wann ich die Schwere

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

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

entfernen sollten Wenn Sie feststellen, eine bessere Lösung, lass es mich wissen :)!

+0

Ich benutze Grenzen, um mein Tablett an Ort und Stelle zu halten. Das Problem beim Hinzufügen von irgendetwas in "startedContactForItem" ist, dass mein Tablett springt, sobald es den unteren Rand des Bildschirms erreicht. So wird der erste Bounce die Gravitation deaktivieren, und er wird nie wieder den Boden erreichen, da er einfach auf den Schwerelosigkeits-Stil aufprallen wird. – Nailer

+0

Und wenn Sie die Objekte vollständig aus dem Animator entfernen, wenn sie den Boden berühren, um die Animation selbst zu beenden? Oder wenn Sie nur ein Element haben, können Sie es vielleicht korrekt durch eine Animation ersetzen, wenn 'dynamicAnimatorDidPause:' ausgelöst wird? – KIDdAe

+0

'dynamicAnimatorDidPause:' Wird nie ausgelöst, aber eine Lösung könnte sein, die Animation nach der ersten Kollision manuell zu beenden. – Nailer

1

Mein Problem ist das gleiche. Mein Animator kommt nie zur Ruhe, also verbraucht meine App 3 bis 4% CPU für immer. Meine Ansichten scheinen sich innerhalb von 1/2 Sekunde nicht mehr zu bewegen. Also, anstatt herauszufinden, warum ich das Gleichgewicht nicht erreicht habe, habe ich es einfach mit einem Hammer geschlagen und den Animator mit einem Timer getötet. Ich gebe es 2 Sekunden.

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