1049 Stimmen

Wie animiere ich Bindungsänderungen?

Ich aktualisiere eine alte Anwendung mit einer AdBannerView und wenn es keine Werbung gibt, wird sie ausgeblendet. Wenn eine Anzeige vorhanden ist, wird sie auf den Bildschirm geschoben. Grundlegende Dinge.

Im alten Stil habe ich den Rahmen in einen Animationsblock gesetzt. Neuer Stil, ich habe eine IBOutlet zur Auto-Layout-Beschränkung, die die Y Position, in diesem Fall den Abstand vom unteren Rand der Superview, und ändern Sie die Konstante:

- (void)moveBannerOffScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = -32;
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    [UIView animateWithDuration:5 animations:^{
        _addBannerDistanceFromBottomConstraint.constant = 0;
    }];
    bannerIsVisible = TRUE;
}

Und das Banner bewegt sich, genau wie erwartet, aber keine Animation.


UPDATE: Ich habe mir die WWDC 12-Talk Best Practices für die Beherrschung des automatischen Layouts die sich mit Animation befasst. Es wird erörtert, wie man Beschränkungen mit CoreAnimation :

Ich habe es mit dem folgenden Code versucht, erhalte aber genau die gleichen Ergebnisse:

- (void)moveBannerOffScreen {
    _addBannerDistanceFromBottomConstraint.constant = -32;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen {
    _addBannerDistanceFromBottomConstraint.constant = 0;
    [UIView animateWithDuration:2 animations:^{
        [self.view setNeedsLayout];
    }];
    bannerIsVisible = TRUE;
}

Nebenbei bemerkt, ich habe das mehrfach überprüft und es wird auf dem メイン Thema.

1813voto

g3rv4 Punkte 19540

Zwei wichtige Hinweise:

  1. Sie müssen anrufen layoutIfNeeded innerhalb des Animationsblocks. Apple empfiehlt sogar, ihn einmal vor dem Animationsblock aufzurufen, um sicherzustellen, dass alle anstehenden Layout-Operationen abgeschlossen sind

  2. Sie müssen es speziell in der übergeordnete Ansicht (z.B.. self.view ), und nicht die untergeordnete Ansicht, der die Beschränkungen zugeordnet sind. Auf diese Weise wird die alle gebundene Ansichten, einschließlich der Animation anderer Ansichten, die an die Ansicht gebunden sind, deren Bindung Sie geändert haben (z. B. Ansicht B ist an der Unterseite von Ansicht A befestigt, und Sie haben gerade den oberen Versatz von Ansicht A geändert und möchten, dass Ansicht B mit ihr animiert wird)

Versuchen Sie dies:

Objektiv-C

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

Mauersegler 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}

117voto

Ich weiß die Antwort zu schätzen, aber ich denke, es wäre schön, wenn wir noch ein bisschen weiter gehen könnten.

Die grundlegende Blockanimation aus der Dokumentation

[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
     // Make all constraint changes here
     [containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];

aber das ist wirklich ein sehr vereinfachtes Szenario. Was ist, wenn ich die Beschränkungen der Unteransicht über die updateConstraints Methode?

Ein Animationsblock, der die Methode updateConstraints der Unteransichten aufruft

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

Die updateConstraints-Methode wird in der UIView-Unterklasse überschrieben und muss am Ende der Methode super aufrufen.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

Das AutoLayout-Handbuch lässt viel zu wünschen übrig, aber es ist lesenswert. Ich selbst verwende es als Teil eines UISwitch die eine Unteransicht mit einem Paar von UITextField s mit einer einfachen und subtilen Animation zum Zusammenklappen (0,2 Sekunden lang). Die Beschränkungen für die Unteransicht werden in den updateConstraints-Methoden der UIView-Unterklassen behandelt, wie oben beschrieben.

85voto

Steven Hepting Punkte 12116

In der Regel müssen Sie nur die Constraints aktualisieren und die layoutIfNeeded innerhalb des Animationsblocks. Dies kann entweder die Änderung des .constant Eigenschaft eines NSLayoutConstraint hinzufügen, Einschränkungen entfernen (iOS 7), oder die .active Eigenschaft von Constraints (iOS 8 & 9).

Beispiel-Code:

[UIView animateWithDuration:0.3 animations:^{
    // Move to right
    self.leadingConstraint.active = false;
    self.trailingConstraint.active = true;

    // Move to bottom
    self.topConstraint.active = false;
    self.bottomConstraint.active = true;

    // Make the animation happen
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

Beispiel-Setup:

Xcode Project so sample animation project.

Kontroverse

Es stellt sich die Frage, ob die Einschränkung geändert werden sollte vor den Animationsblock, oder innerhalb es (siehe vorherige Antworten).

Es folgt eine Twitter-Konversation zwischen Martin Pilkington, der iOS unterrichtet, und Ken Ferry, der Auto Layout geschrieben hat. Ken erklärt, dass durch das Ändern von Konstanten außerhalb des Animationsblocks kann derzeit Arbeit, es ist nicht sicher, und sie sollten wirklich geändert werden innerhalb den Animationsblock. https://twitter.com/kongtomorrow/status/440627401018466305

Animation:

Beispielprojekt

Hier ist ein einfaches Projekt, das zeigt, wie eine Ansicht animiert werden kann. Es verwendet Objective C und animiert die Ansicht durch Änderung der .active Eigenschaft mehrerer Constraints. https://github.com/shepting/SampleAutoLayoutAnimation

40voto

John Erck Punkte 9350
// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)

// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{

    // Step 3, call layoutIfNeeded on your animated view's parent
    [self.view layoutIfNeeded];
}];

33voto

Milan Nosáľ Punkte 18166

Swift 4 Lösung

UIView.animate

Drei einfache Schritte:

  1. Ändern Sie die Einschränkungen, z. B.:

    heightAnchor.constant = 50
  2. Sagen Sie dem Inhalt view dass sein Layout verschmutzt ist und dass das Autolayout das Layout neu berechnen soll:

    self.view.setNeedsLayout()
  3. Im Animationsblock weisen Sie das Layout an, das Layout neu zu berechnen, was dem direkten Setzen der Rahmen entspricht (in diesem Fall wird das Autolayout die Rahmen setzen):

    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }

Vervollständigen Sie das einfachste Beispiel:

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

Sidenote

Es gibt einen optionalen 0. Schritt - bevor Sie die Beschränkungen ändern, können Sie die self.view.layoutIfNeeded() um sicherzustellen, dass der Startpunkt für die Animation von dem Zustand ausgeht, auf den die alten Beschränkungen angewandt wurden (für den Fall, dass andere Beschränkungen geändert wurden, die nicht in die Animation einbezogen werden sollten):

otherConstraint.constant = 30
// this will make sure that otherConstraint won't be animated but will take effect immediately
self.view.layoutIfNeeded()

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

UIViewPropertyAnimator

Seit iOS 10 haben wir einen neuen Animationsmechanismus - UIViewPropertyAnimator sollten wir wissen, dass im Grunde genommen derselbe Mechanismus dafür gilt. Die Schritte sind im Grunde die gleichen:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

Seit animator eine Kapselung der Animation ist, können wir einen Verweis darauf behalten und sie später aufrufen. Da wir jedoch im Animationsblock dem Autolayout nur sagen, dass es die Frames neu berechnen soll, müssen wir die Constraints ändern, bevor wir startAnimation . Daher ist so etwas möglich:

// prepare the animator first and keep a reference to it
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}

// at some other point in time we change the constraints and call the animator
heightAnchor.constant = 50
self.view.setNeedsLayout()
animator.startAnimation()

Die Reihenfolge des Änderns von Beschränkungen und des Startens eines Animators ist wichtig - wenn wir nur die Beschränkungen ändern und unseren Animator auf einen späteren Zeitpunkt verschieben, kann der nächste Redraw-Zyklus eine automatische Neuberechnung des Layouts auslösen und die Änderung wird nicht animiert.

Denken Sie auch daran, dass ein einzelner Animator nicht wiederverwendbar ist - wenn Sie ihn einmal ausgeführt haben, können Sie ihn nicht noch einmal "starten". Es gibt also nicht wirklich einen guten Grund, den Animator zu behalten, es sei denn, wir verwenden ihn zur Steuerung einer interaktiven Animation.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X