Storyboard, Code, Tipps und ein paar Fallstricke
Die anderen Antworten sind in Ordnung, aber diese zeigt einige ziemlich wichtige Fallstricke beim Animieren von Einschränkungen anhand eines aktuellen Beispiels. Ich habe viele Variationen durchlaufen, bevor mir Folgendes klar wurde:
Machen Sie die Einschränkungen, auf die Sie abzielen möchten, zu Klassenvariablen, um eine starke Referenz zu erhalten. In Swift habe ich faule 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 stellte ich fest, dass man die Einschränkung aus der Ansicht OBEN (auch bekannt als Übersicht) der beiden Ansichten erhalten muss, in denen die Einschränkung definiert ist. Im folgenden Beispiel (sowohl MNGStarRating als auch UIWebView sind die beiden Arten von Elementen, zwischen denen ich eine Einschränkung erstelle, und sie sind Unteransichten in self.view).
Filterverkettung
Ich nutze die Filtermethode von Swift, um die gewünschte Einschränkung zu trennen, die als Wendepunkt dient. Man könnte auch viel komplizierter werden, aber Filter macht hier einen guten Job.
Animieren von Einschränkungen mit Swift
Nota Bene - Dieses Beispiel ist die Storyboard- / Codelösung und setzt voraus, dass im Storyboard Standardeinschränkungen festgelegt wurden. Man kann die Änderungen dann mit Code animieren.
Angenommen, Sie erstellen eine Eigenschaft zum Filtern nach genauen Kriterien und gelangen zu einem bestimmten Wendepunkt für Ihre Animation (natürlich können Sie auch nach einem Array filtern und eine Schleife durchführen, 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!
}()
....
Etwas 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 Wendungen
Diese Notizen sind wirklich eine Reihe von Tipps, die ich für mich selbst geschrieben habe. Ich habe alle Dinge persönlich und schmerzhaft getan. Hoffentlich kann dieser Leitfaden andere schonen.
Achten Sie auf zPositioning. Manchmal, wenn anscheinend nichts passiert, sollten Sie einige der anderen Ansichten ausblenden oder den Ansichtsdebugger verwenden, um Ihre animierte Ansicht zu finden. Ich habe sogar Fälle gefunden, in denen ein benutzerdefiniertes Laufzeitattribut in der XML-Datei eines Storyboards verloren ging und dazu führte, dass die animierte Ansicht (während der Arbeit) abgedeckt wurde.
Nehmen Sie sich immer eine Minute Zeit, um die Dokumentation (neu und alt), die Schnellhilfe und die Überschriften zu lesen. Apple nimmt ständig Änderungen vor, um die AutoLayout-Einschränkungen besser zu verwalten (siehe Stapelansichten). Oder zumindest das AutoLayout-Kochbuch . Denken Sie daran, dass die besten Lösungen manchmal in den älteren Dokumentationen / Videos enthalten sind.
Spielen Sie mit den Werten in der Animation und erwägen Sie die Verwendung anderer animateWithDuration-Varianten.
Codieren Sie bestimmte Layoutwerte nicht als Kriterien zum Bestimmen von Änderungen an anderen Konstanten, sondern verwenden Sie Werte, mit denen Sie den Speicherort der Ansicht bestimmen können. CGRectContainsRect
ist ein Beispiel
- Zögern Sie nicht, bei Bedarf die Layoutränder zu verwenden, die einer Ansicht zugeordnet sind, die an der Einschränkungsdefinition teilnimmt
let viewMargins = self.webview.layoutMarginsGuide
: ist ein Beispiel
- Machen Sie keine Arbeit, die Sie nicht tun müssen. Alle Ansichten mit Einschränkungen im Storyboard haben Einschränkungen, die an die Eigenschaft self.viewName.constraints angehängt sind
- Ändern Sie Ihre Prioritäten für Einschränkungen auf weniger als 1000. Ich habe meine im Storyboard auf 250 (niedrig) oder 750 (hoch) gesetzt. (Wenn Sie versuchen, eine 1000-Priorität in einen Code zu ändern, stürzt die App ab, da 1000 erforderlich ist.)
- Versuchen Sie nicht sofort, activConstraints und disableConstraints zu verwenden (sie haben ihren Platz, aber wenn Sie nur lernen oder wenn Sie ein Storyboard verwenden, bedeutet dies wahrscheinlich, dass Sie zu viel tun - sie haben jedoch einen Platz, wie unten gezeigt).
- Verwenden Sie addConstraints / removeConstraints nur, wenn Sie dem Code wirklich eine neue Einschränkung hinzufügen. Ich habe festgestellt, dass ich die Ansichten im Storyboard meistens mit den gewünschten Einschränkungen (Platzieren der Ansicht außerhalb des Bildschirms) gestalte und dann im Code die zuvor im Storyboard erstellten Einschränkungen animiere, um die Ansicht zu verschieben.
- Ich habe viel Zeit damit verschwendet, Einschränkungen mit der neuen NSAnchorLayout-Klasse und den Unterklassen aufzubauen. Diese funktionieren einwandfrei, aber es hat eine Weile gedauert, bis mir klar wurde, dass alle Einschränkungen, die ich brauchte, bereits im Storyboard vorhanden waren. Wenn Sie Einschränkungen in Code erstellen, verwenden Sie diese Methode mit Sicherheit, um Ihre Einschränkungen zu aggregieren:
Schnelle Auswahl an 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 das layoutIfNeeded hinzugefügt werden soll, passiert höchstwahrscheinlich nichts: In diesem Fall haben Sie möglicherweise eine halbherzige Lösung wie diese:
NB - Nehmen Sie sich einen Moment Zeit, um den Abschnitt AutoLayout unten und die Originalanleitung zu lesen. Es gibt eine Möglichkeit, diese Techniken zu verwenden, 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
}
}
Snippet aus dem AutoLayout-Handbuch (beachten Sie, dass das zweite Snippet für die Verwendung von OS X vorgesehen ist). Übrigens - Soweit ich sehen kann, ist dies nicht mehr im aktuellen Handbuch enthalten. Die bevorzugten Techniken entwickeln sich weiter.
Animieren von Änderungen, die durch das automatische Layout vorgenommen wurden
Wenn Sie die vollständige Kontrolle über das Animieren von Änderungen benötigen, die von Auto Layout vorgenommen wurden, müssen Sie Ihre Einschränkungsänderungen programmgesteuert vornehmen. Das Grundkonzept ist für iOS und OS X gleich, es gibt jedoch einige geringfügige Unterschiede.
In einer iOS-App sieht Ihr Code folgendermaßen aus:
[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 in OS X den folgenden Code, wenn Sie Animationen mit Ebenen verwenden:
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
Wenn Sie keine Animationen mit Ebenenunterstützung verwenden, müssen Sie die Konstante mit dem Animator der Einschränkung animieren:
[[constraint animator] setConstant:42];
Für diejenigen, die visuell besser lernen, sehen Sie sich dieses frühe Video von Apple an .
Pass gut auf
Oft gibt es in der Dokumentation kleine Notizen oder Codeteile, die zu größeren Ideen führen. Das Anhängen von Einschränkungen für das automatische Layout an dynamische Animatoren ist beispielsweise eine große Idee.
Viel Glück und möge die Macht mit dir sein.