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.

19voto

Nazariy Vlizlo Punkte 732

Eine schnelle Lösung:

yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})

16voto

Tommie C. Punkte 12311

Storyboard, Code, Tipps und ein paar Tücken

Die anderen Antworten sind in Ordnung, aber diese hier hebt ein paar ziemlich wichtige Probleme bei der Animation von Beschränkungen anhand eines aktuellen Beispiels hervor. Ich habe viele Variationen durchgespielt, bevor ich das Folgende erkannte:

Machen Sie die Einschränkungen, auf die Sie abzielen wollen, zu Klassenvariablen, die eine starke Referenz enthalten. In Swift habe ich Lazy-Variablen verwendet:

lazy var centerYInflection:NSLayoutConstraint = {
       let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
        return temp!
}()

Nach einigen Experimenten habe ich festgestellt, dass man die Einschränkung aus der Ansicht erhalten MUSS OBEN (auch Superview genannt) die beiden Ansichten, in denen die Einschränkung definiert ist. Im folgenden Beispiel (MNGStarRating und UIWebView sind die beiden Arten von Elementen, zwischen denen ich eine Einschränkung erstelle, und sie sind Unteransichten innerhalb von self.view).

Filterverkettung

Ich nutze die Filtermethode von Swift, um die gewünschte Beschränkung, die als Wendepunkt dienen soll, abzutrennen. Man könnte auch viel komplizierter werden, aber Filter leistet hier gute Arbeit.

Animieren von Beschränkungen mit Swift

Nota Bene - Dieses Beispiel dass man im Storyboard Standardeinschränkungen vorgenommen hat. Man kann dann die Änderungen mit Code animieren.

Angenommen, Sie erstellen eine Eigenschaft, um nach genauen Kriterien zu filtern und einen bestimmten Wendepunkt für Ihre Animation zu erreichen (natürlich können Sie auch nach einem Array filtern und eine Schleife durchlaufen, wenn Sie mehrere Einschränkungen benötigen):

lazy var centerYInflection:NSLayoutConstraint = {
    let temp =  self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
    return temp!
}()

....

Irgendwann später...

@IBAction func toggleRatingView (sender:AnyObject){

    let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)

    self.view.layoutIfNeeded()

    //Use any animation you want, I like the bounce in springVelocity...
    UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in

        //I use the frames to determine if the view is on-screen
        if CGRectContainsRect(self.view.frame, self.ratingView.frame) {

            //in frame ~ animate away
            //I play a sound to give the animation some life

            self.centerYInflection.constant = aPointAboveScene
            self.centerYInflection.priority = UILayoutPriority(950)

        } else {

            //I play a different sound just to keep the user engaged
            //out of frame ~ animate into scene
            self.centerYInflection.constant = 0
            self.centerYInflection.priority = UILayoutPriority(950)
            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()
         }) { (success) -> Void in

            //do something else

        }
    }
}

Die vielen falschen Abzweigungen

Diese Notizen sind eigentlich eine Sammlung von Tipps, die ich für mich selbst geschrieben habe. Ich habe all diese Tipps persönlich und unter Schmerzen umgesetzt. Hoffentlich kann dieser Leitfaden anderen helfen.

  1. Achten Sie auf die zPositionierung. Manchmal, wenn scheinbar nichts passiert passiert, sollten Sie einige der anderen Ansichten ausblenden oder den View Debugger verwenden, um Ihre animierte Ansicht zu lokalisieren. Ich habe sogar Fälle gefunden, in denen ein User Defined Runtime Attribut in der xml eines Storyboards verloren ging und dazu führte, dass die animierte Ansicht verdeckt wurde (während sie funktionierte).

  2. Nehmen Sie sich immer eine Minute Zeit, um die Dokumentation (neu und alt) zu lesen, Quick Hilfe und die Kopfzeilen. Apple nimmt viele Änderungen vor, um die AutoLayout-Einschränkungen zu verwalten (siehe Stapelansichten). Oder zumindest die AutoLayout-Kochbuch . Denken Sie daran, dass die besten Lösungen manchmal in älteren Dokumentationen/Videos zu finden sind.

  3. Spielen Sie mit den Werten in der Animation herum und erwägen Sie die Verwendung von andere animateWithDuration-Varianten.

  4. Codieren Sie keine spezifischen Layoutwerte als Kriterien für die Bestimmung von Änderungen an anderen Konstanten, sondern verwenden Sie Werte, mit denen Sie die Lage der Ansicht zu bestimmen. CGRectContainsRect ist eine Beispiel

  5. Zögern Sie nicht, bei Bedarf die Layout-Ränder zu verwenden, die mit einer an der Constraint-Definition beteiligten Ansicht let viewMargins = self.webview.layoutMarginsGuide : ist ein Beispiel

  6. Erledigen Sie keine Arbeiten, die Sie nicht erledigen müssen, alle Ansichten mit Beschränkungen auf die Storyboard haben Constraints, die mit der Eigenschaft self.viewName.constraints

  7. Ändern Sie Ihre Prioritäten für alle Beschränkungen auf weniger als 1000. Ich setze meine im Storyboard auf 250 (niedrig) oder 750 (hoch); (wenn Sie versuchen, eine Priorität von 1000 im Code zu ändern, stürzt die Anwendung ab, weil 1000 erforderlich ist)

  8. Erwägen Sie, nicht sofort zu versuchen, activateConstraints und deactivateConstraints zu verwenden (sie haben ihren Platz, aber wenn man gerade lernt oder ein Storyboard verwendet, bedeutet das wahrscheinlich, dass man zu viel macht ~ sie haben ihren Platz, wie unten zu sehen ist)

  9. Erwägen Sie, addConstraints / removeConstraints nicht zu verwenden, es sei denn, Sie sind wirklich eine neue Einschränkung im Code hinzufügen. Ich habe festgestellt, dass ich die meiste Zeit das Layout der Ansichten im Storyboard mit den gewünschten Constraints (Platzierung die Ansicht außerhalb des Bildschirms platziere), und dann im Code die zuvor im Storyboard erstellten Constraints animiere, um die Ansicht zu verschieben.

  10. Ich habe viel Zeit damit vergeudet, mit dem neuen Programm Einschränkungen aufzubauen. NSAnchorLayout-Klasse und Unterklassen. Diese funktionieren sehr gut, aber es aber ich brauchte eine Weile, um zu erkennen, dass alle Einschränkungen, die ich brauchte bereits im Storyboard vorhanden waren. Wenn Sie Beschränkungen im Code erstellen erstellen, sollten Sie auf jeden Fall diese Methode verwenden, um Ihre Beschränkungen zusammenzufassen:

Schnelles Beispiel für Lösungen, die bei der Verwendung von Storyboards vermieden werden sollten

private var _nc:[NSLayoutConstraint] = []
    lazy var newConstraints:[NSLayoutConstraint] = {

        if !(self._nc.isEmpty) {
            return self._nc
        }

        let viewMargins = self.webview.layoutMarginsGuide
        let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)

        let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
        centerY.constant = -1000.0
        centerY.priority = (950)
        let centerX =  self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
        centerX.priority = (950)

        if let buttonConstraints = self.originalRatingViewConstraints?.filter({

            ($0.firstItem is UIButton || $0.secondItem is UIButton )
        }) {
            self._nc.appendContentsOf(buttonConstraints)

        }

        self._nc.append( centerY)
        self._nc.append( centerX)

        self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
        self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
        self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))

        return self._nc
    }()

Wenn Sie einen dieser Tipps oder die einfacheren Tipps vergessen, z. B. wo Sie layoutIfNeeded einfügen, wird höchstwahrscheinlich nichts passieren: In diesem Fall haben Sie vielleicht eine halbgare Lösung wie diese:

NB - Nehmen Sie sich einen Moment Zeit und lesen Sie die AutoLayo Original-Leitfaden. Es gibt eine Möglichkeit, diese Techniken zu nutzen, um Ihre Dynamischen Animatoren zu ergänzen.

UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in

            //
            if self.starTopInflectionPoint.constant < 0  {
                //-3000
                //offscreen
                self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
                self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)

            } else {

                self.starTopInflectionPoint.constant = -3000
                 self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
            }

        }) { (success) -> Void in

            //do something else
        }

    }

Ausschnitt aus dem AutoLayout-Handbuch (beachten Sie, dass der zweite Ausschnitt für die Verwendung von OS X gilt). Dies ist übrigens nicht mehr im aktuellen Leitfaden enthalten, soweit ich sehen kann. Die bevorzugten Techniken entwickeln sich ständig weiter.

Animieren von Änderungen durch automatisches Layout

Wenn Sie die volle Kontrolle über die von Auto-Layout vorgenommenen Änderungen haben möchten, müssen Sie die Änderungen an den Beschränkungen programmatisch vornehmen. Das Grundkonzept ist für iOS und OS X gleich, aber es gibt ein paar kleine Unterschiede.

In einer iOS-App würde Ihr Code etwa wie folgt aussehen:

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

Verwenden Sie unter OS X den folgenden Code, wenn Sie Animationen mit Ebenenunterstützung verwenden:

[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     [context setAllowsImplicitAnimation: YES];
     // Make all constraint changes here
     [containerView layoutSubtreeIfNeeded];
}];

Wenn Sie keine ebenengestützten Animationen verwenden, müssen Sie die Konstante mit dem Animator der Beschränkung animieren:

[[constraint animator] setConstant:42];

Diejenigen, die besser visuell lernen, sollten sich diese frühe Video von Apple .

Aufmerksam sein

Oft finden sich in der Dokumentation kleine Hinweise oder Codestücke, die zu größeren Ideen führen. Zum Beispiel ist das Anhängen von Auto-Layout-Beschränkungen an dynamische Animatoren eine wichtige Idee.

Viel Glück und möge die Macht mit Ihnen sein.

14voto

C0mrade Punkte 1105

100%ige Arbeitslösung Swift 5.3

Ich habe alle Antworten gelesen und möchte den Code und die Hierarchie der Zeilen teilen, die ich in allen meinen Anwendungen verwendet habe, um sie korrekt zu animieren. Einige Lösungen hier funktionieren nicht, Sie sollten sie auf langsameren Geräten, z.B. iPhone 5, im Moment überprüfen.

self.btnHeightConstraint.constant = 110
UIView.animate(withDuration: 0.27) { [weak self] in 
     self?.view.layoutIfNeeded()
}

5voto

Gabriel.Massana Punkte 8067

Ich habe versucht, Constraints zu animieren, und es war nicht wirklich einfach, eine gute Erklärung zu finden.

Was andere Antworten sagen, ist völlig richtig: Sie müssen anrufen [self.view layoutIfNeeded]; innerhalb animateWithDuration: animations: . Der andere wichtige Punkt ist jedoch, dass es Zeiger für jede NSLayoutConstraint die Sie animieren möchten.

Ich habe ein Beispiel auf GitHub erstellt .

4voto

Edoardo Vicoli Punkte 227

Funktionierende und gerade getestete Lösung für Swift 3 mit Xcode 8.3.3:

self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0

UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

Denken Sie daran, dass self.calendarViewHeight eine Einschränkung ist, die sich auf eine CustomView (CalendarView) bezieht. Ich habe die Funktion .layoutIfNeeded() für self.view und NICHT für self.calendarView aufgerufen.

Ich hoffe, das hilft.

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