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.
-
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).
-
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.
-
Spielen Sie mit den Werten in der Animation herum und erwägen Sie die Verwendung von andere animateWithDuration-Varianten.
-
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
-
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
-
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
-
Ä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)
-
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)
-
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.
-
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.