Das Erstellen von Animationen ist großartig. Sie sind ein wichtiger Bestandteil der
Richtlinien für die
Benutzeroberfläche von
iOS . Animationen helfen dabei, die Aufmerksamkeit des Benutzers auf wichtige Dinge zu lenken oder die Anwendung einfach weniger langweilig zu machen.
Es gibt verschiedene Möglichkeiten, Animationen in iOS zu implementieren. Der wahrscheinlich beliebteste Weg ist die Verwendung von
UIView.animate (withDuration: animations :) . Sie können eine Bildebene mit
CABasicAnimation animieren. Darüber hinaus können Sie mit UIKit benutzerdefinierte Animationen konfigurieren, um den Controller mithilfe von
UIViewControllerTransitioningDelegate anzuzeigen .
In diesem Artikel möchte ich einen weiteren aufregenden Weg zum
Animieren von Ansichten diskutieren -
UIViewPropertyAnimator . Diese Klasse bietet viel mehr Verwaltungsfunktionen als ihr Vorgänger
UIView.animat e. Verwenden Sie diese Option, um temporäre, interaktive und unterbrochene Animationen zu erstellen. Außerdem ist es möglich, den Animator schnell zu ändern.
Einführung in UIViewPropertyAnimator
UIViewPropertyAnimator wurde in
iOS 10 eingeführt . Sie können damit objektorientiert Animationen erstellen. Schauen wir uns ein Beispiel für eine Animation an, die mit dem
UIViewPropertyAnimator erstellt wurde .

So war es bei der Verwendung von UIView.
UIView.animate(withDuration: 0.3) { view.frame = view.frame.offsetBy(dx: 100, dy: 0) }
Und so geht's mit dem
UIViewPropertyAnimator :
let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { view.frame = view.frame.offsetBy(dx:100, dy:0) } animator.startAnimation()
Wenn Sie die Animation überprüfen müssen, erstellen Sie einfach einen Spielplatz und führen Sie den Code wie unten gezeigt aus. Beide Codefragmente führen zum gleichen Ergebnis.

Sie könnten denken, dass es in diesem Beispiel keinen großen Unterschied gibt. Was bringt es also, eine neue Methode zum Erstellen von Animationen hinzuzufügen?
UIViewPropertyAnimator wird nützlicher, wenn Sie interaktive Animationen erstellen müssen.
Interaktive und unterbrochene Animation
Erinnern Sie sich an die klassische Geste „Fingerwischen zum Entsperren des Geräts“? Oder die Geste "Bewegen Sie Ihren Finger auf dem Bildschirm von unten nach oben", um das Control Center zu öffnen? Dies sind großartige Beispiele für interaktive Animationen. Sie können das Bild mit dem Finger bewegen und dann loslassen. Das Bild kehrt dann in seine ursprüngliche Position zurück. Außerdem können Sie das Bild während der Animation erfassen und mit dem Finger weiter bewegen.
UIView-Animationen bieten keine einfache Möglichkeit, den Prozentsatz der Fertigstellung der Animation zu steuern. Sie können die Animation nicht in der Mitte einer Schleife anhalten und die Ausführung nach einer Unterbrechung fortsetzen.
In diesem Fall werden wir über
UIViewPropertyAnimator sprechen. Als Nächstes sehen wir uns an, wie Sie in wenigen Schritten auf einfache Weise vollständig interaktive, unterbrochene Animationen und umgekehrte Animationen erstellen können.
Vorbereitung des Startprojekts
Zuerst müssen Sie
das Starterprojekt herunterladen .
Sobald Sie das Archiv geöffnet haben, finden Sie die
CityGuide- Anwendung, mit der Benutzer ihre Ferien planen können. Der Benutzer kann durch die Liste der Städte scrollen und dann eine detaillierte Beschreibung mit detaillierten Informationen über die Stadt öffnen, die ihm gefällt.
Berücksichtigen Sie den Quellcode des Projekts, bevor Sie mit der Erstellung schöner Animationen beginnen. Folgendes können Sie in einem Projekt finden, indem Sie es in
Xcode öffnen:
- ViewController.swift : Der Hauptanwendungscontroller mit einer UICollectionView , die ein Array von Stadtobjekten anzeigt.
- CityCollectionViewCell.swift: Zelle zum Anzeigen von City . Tatsächlich gelten in diesem Artikel die meisten Änderungen für diese Klasse. Möglicherweise stellen Sie fest, dass descriptionLabel und closeButton bereits in der Klasse definiert sind. Nach dem Starten der Anwendung werden diese Objekte jedoch ausgeblendet. Keine Sorge, sie werden etwas später sichtbar sein. Diese Klasse verfügt auch über die Eigenschaften collectionView und index . Später werden sie für die Animation verwendet.
- CityCollectionViewFlowLayout.swift: Diese Klasse ist für das horizontale Scrollen verantwortlich. Wir werden es noch nicht ändern.
- City.swift : Das Hauptanwendungsmodell verfügt über eine Methode, die in ViewController verwendet wurde.
- Main.storyboard: Dort finden Sie die Benutzeroberfläche für ViewController und CityCollectionViewCell .
Versuchen wir, die Beispielanwendung zu erstellen und auszuführen. Als Ergebnis erhalten wir Folgendes.
Implementierung der Bereitstellungs- und Reduzierungsanimation
Nach dem Start der Anwendung wird eine Liste der Städte angezeigt. Der Benutzer kann jedoch nicht mit Objekten in Form von Zellen interagieren. Jetzt müssen Sie Informationen für jede Stadt anzeigen, wenn der Benutzer auf eine der Zellen klickt. Schauen Sie sich die endgültige Version der Anwendung an. Folgendes musste tatsächlich entwickelt werden:
Die Animation sieht gut aus, nicht wahr? Aber hier gibt es nichts Besonderes, es ist nur die Grundlogik des
UIViewPropertyAnimator . Mal sehen, wie diese Art von Animation implementiert wird. Erstellen Sie eine
collectionView- Methode
(_: didSelectItemAt) und fügen Sie das folgende Codefragment am Ende der
ViewController- Datei hinzu:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedCell = collectionView.cellForItem(at: indexPath)! as! CityCollectionViewCell selectedCell.toggle() }
Jetzt müssen wir die
Umschaltmethode implementieren.
Wechseln wir zu
CityCollectionViewCell.swift und implementieren Sie diese Methode.
Fügen Sie zunächst die
Statusaufzählung am Anfang der Datei direkt vor der Deklaration der
CityCollectionViewCell- Klasse hinzu. Mit dieser Auflistung können Sie den Status einer Zelle verfolgen:
private enum State { case expanded case collapsed var change: State { switch self { case .expanded: return .collapsed case .collapsed: return .expanded } } }
Fügen Sie einige Eigenschaften hinzu, um die Animation in der
CityCollectionViewCell- Klasse zu steuern:
private var initialFrame: CGRect? private var state: State = .collapsed private lazy var animator: UIViewPropertyAnimator = { return UIViewPropertyAnimator(duration: 0.3, curve: .easeInOut) }()
Die Variable
initialFrame wird verwendet, um den Zellenrahmen zu speichern, bis die Animation ausgeführt wird.
Der Status wird verwendet, um zu verfolgen, ob die Zelle erweitert oder reduziert ist. Die
Animatorvariable wird zur Steuerung der Animation verwendet.
Fügen Sie nun die
Umschaltmethode hinzu und rufen Sie sie von der
close- Methode aus auf, zum Beispiel:
@IBAction func close(_ sender: Any) { toggle() } func toggle() { switch state { case .expanded: collapse() case .collapsed: expand() } }
Dann fügen wir zwei weitere Methoden hinzu:
expand () undapse () . Wir werden ihre Umsetzung fortsetzen. Zunächst beginnen wir mit der Methode
expansiond () :
private func expand() { guard let collectionView = self.collectionView, let index = self.index else { return } animator.addAnimations { self.initialFrame = self.frame self.descriptionLabel.alpha = 1 self.closeButton.alpha = 1 self.layer.cornerRadius = 0 self.frame = CGRect(x: collectionView.contentOffset.x, y:0, width: collectionView.frame.width, height: collectionView.frame.height) if let leftCell = collectionView.cellForItem(at: IndexPath(row: index - 1, section: 0)) { leftCell.center.x -= 50 } if let rightCell = collectionView.cellForItem(at: IndexPath(row: index + 1, section: 0)) { rightCell.center.x += 50 } self.layoutIfNeeded() } animator.addCompletion { position in switch position { case .end: self.state = self.state.change collectionView.isScrollEnabled = false collectionView.allowsSelection = false default: () } } animator.startAnimation() }
Wie viel Code. Lassen Sie mich Schritt für Schritt erklären, was passiert:
- Überprüfen Sie zunächst, ob collectionView und index ungleich Null sind. Andernfalls können wir die Animation nicht starten.
- Beginnen Sie als Nächstes mit der Erstellung der Animation, indem Sie animator.addAnimations aufrufen.
- Speichern Sie als Nächstes den aktuellen Frame, mit dem er in der Faltungsanimation wiederhergestellt wird.
- Dann setzen wir den Alpha-Wert für descriptionLabel und closeButton , um sie sichtbar zu machen.
- Entfernen Sie als Nächstes die abgerundete Ecke und legen Sie einen neuen Rahmen für die Zelle fest. Die Zelle wird im Vollbildmodus angezeigt.
- Als nächstes bewegen wir die Nachbarzellen.
- Rufen Sie nun die Methode animator.addComplete () auf, um die Interaktion des Sammlungsbildes zu deaktivieren. Dies verhindert, dass Benutzer während der Zellenerweiterung einen Bildlauf durchführen. Ändern Sie auch den aktuellen Status der Zelle. Es ist wichtig, den Status der Zelle zu ändern. Erst danach endet die Animation.
Fügen Sie nun eine Faltungsanimation hinzu. Kurz gesagt, es ist nur so, dass wir die Zelle in ihren vorherigen Zustand zurückversetzen:
private func collapse() { guard let collectionView = self.collectionView, let index = self.index else { return } animator.addAnimations { self.descriptionLabel.alpha = 0 self.closeButton.alpha = 0 self.layer.cornerRadius = self.cornerRadius self.frame = self.initialFrame! if let leftCell = collectionView.cellForItem(at: IndexPath(row: index - 1, section: 0)) { leftCell.center.x += 50 } if let rightCell = collectionView.cellForItem(at: IndexPath(row: index + 1, section: 0)) { rightCell.center.x -= 50 } self.layoutIfNeeded() } animator.addCompletion { position in switch position { case .end: self.state = self.state.change collectionView.isScrollEnabled = true collectionView.allowsSelection = true default: () } } animator.startAnimation() }
Jetzt ist es Zeit, die Anwendung zu kompilieren und auszuführen. Wenn Sie auf die Zelle klicken, wird die Animation angezeigt. Um das Bild zu schließen, klicken Sie auf das Kreuzsymbol in der oberen rechten Ecke.
Hinzufügen einer Gestenverarbeitung
Sie können behaupten, mit
UIView.animate dasselbe Ergebnis zu
erzielen . Was
bringt es , einen
UIViewPropertyAnimator zu verwenden ?
Nun, es ist Zeit, die Animation interaktiv zu gestalten. Fügen Sie einen
UIPanGestureRecognizer und eine neue Eigenschaft namens
popupOffset hinzu, um zu verfolgen, wie viel die Zelle verschoben werden kann.
Deklarieren wir diese Variablen in der
CityCollectionViewCell- Klasse:
private let popupOffset: CGFloat = (UIScreen.main.bounds.height - cellSize.height)/2.0 private lazy var panRecognizer: UIPanGestureRecognizer = { let recognizer = UIPanGestureRecognizer() recognizer.addTarget(self, action: #selector(popupViewPanned(recognizer:))) return recognizer }()
Fügen Sie dann die folgende Methode hinzu, um die Swipe-Definition zu registrieren:
override func awakeFromNib() { self.addGestureRecognizer(panRecognizer) }
Jetzt müssen Sie die
popupViewPanned- Methode hinzufügen, um die
Wischgeste zu verfolgen. Fügen Sie den folgenden Code in die
CityCollectionViewCell ein :
@objc func popupViewPanned(recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: toggle() animator.pauseAnimation() case .changed: let translation = recognizer.translation(in: collectionView) var fraction = -translation.y / popupOffset if state == .expanded { fraction *= -1 } animator.fractionComplete = fraction case .ended: animator.continueAnimation(withTimingParameters: nil, durationFactor: 0) default: () } }
Es gibt drei Zustände. Zu Beginn der Geste initialisieren wir den Animator mit der
Umschaltmethode und halten ihn sofort an. Während der Benutzer die Zelle zieht, aktualisieren wir die Animation, indem wir die Eigenschaften des
FractionComplete- Multiplikators festlegen. Dies ist die Hauptmagie des Animators, mit der er steuern kann. Wenn der Benutzer seinen Finger loslässt, wird die animator-Methode continueAnimation aufgerufen, um die Animation fortzusetzen. Dann bewegt sich die Zelle zur Zielposition.
Nach dem Starten der Anwendung können Sie die Zelle nach oben ziehen, um sie zu erweitern. Ziehen Sie dann die erweiterte Zelle nach unten, um sie zu reduzieren.
Die Animation sieht jetzt ziemlich gut aus, aber eine Unterbrechung der Animation in der Mitte ist nicht möglich. Um die Animation vollständig interaktiv zu gestalten, müssen Sie daher eine weitere Funktion hinzufügen - die Unterbrechung. Der Benutzer kann die Erweiterungs- / Reduzierungsanimation wie gewohnt starten. Die Animation sollte jedoch sofort angehalten werden, nachdem der Benutzer während des Animationszyklus auf die Zelle geklickt hat.
Dazu müssen Sie den Fortschritt der Animation speichern und diesen Wert berücksichtigen, um den Prozentsatz der Fertigstellung der Animation zu berechnen.
Deklarieren Sie zunächst eine neue Eigenschaft in
CityCollectionViewCell :
private var animationProgress: CGFloat = 0
Aktualisieren Sie dann den
.began- Block der
popupViewPanned- Methode mit der folgenden Codezeile, um sich den Fortschritt zu merken:
animationProgress = animator.fractionComplete
Im Block
.changed müssen
Sie die folgende Codezeile aktualisieren, um den Fertigstellungsgrad korrekt zu berechnen:
animator.fractionComplete = fraction + animationProgress
Jetzt ist die Anwendung zum Testen bereit. Führen Sie das Projekt aus und sehen Sie, was passiert. Wenn alle Aktionen gemäß meinen Anweisungen korrekt ausgeführt werden, sollte die Animation folgendermaßen aussehen:
Animation umgekehrt
Sie können einen Fehler für die aktuelle Implementierung finden. Wenn Sie die Zelle ein wenig ziehen und dann an ihre ursprüngliche Position zurückbringen, wird die Zelle weiter erweitert, wenn Sie Ihren Finger loslassen. Beheben wir dieses Problem, um die interaktive Animation noch besser zu machen.
Aktualisieren wir den
.end- Block der
popupViewPanned- Methode wie unten beschrieben:
let velocity = recognizer.velocity(in: self) let shouldComplete = velocity.y > 0 if velocity.y == 0 { animator.continueAnimation(withTimingParameters: nil, durationFactor: 0) break } switch state { case .expanded: if !shouldComplete && !animator.isReversed { animator.isReversed = !animator.isReversed } if shouldComplete && animator.isReversed { animator.isReversed = !animator.isReversed } case .collapsed: if shouldComplete && !animator.isReversed { animator.isReversed = !animator.isReversed } if !shouldComplete && animator.isReversed { animator.isReversed = !animator.isReversed } } animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
Jetzt berücksichtigen wir die Geschwindigkeit der Geste, um zu bestimmen, ob die Animation umgekehrt werden soll.
Fügen Sie abschließend eine weitere Codezeile in den
.changed- Block ein. Setzen Sie diesen Code rechts neben die Berechnung von
animator.fractionComplete .
if animator.isReversed { fraction *= -1 }
Lassen Sie uns die Anwendung erneut ausführen. Jetzt sollte alles ohne Fehler funktionieren.
Pan-Geste korrigieren
Daher haben wir die Implementierung der Animation mit dem
UIViewPropertyAnimator abgeschlossen . Es gibt jedoch einen unangenehmen Fehler. Möglicherweise haben Sie sie beim Testen der Anwendung getroffen. Das Problem ist, dass ein horizontales Scrollen der Zelle nicht möglich ist. Versuchen wir, nach links / rechts durch die Zellen zu streichen, und wir werden mit dem Problem konfrontiert.
Der Hauptgrund hängt mit dem
von uns
erstellten UIPanGestureRecognizer zusammen . Es fängt auch eine Wischgeste ab und steht in Konflikt mit der integrierten Gestenerkennung
UICollectionView .
Obwohl der Benutzer immer noch durch den oberen / unteren Rand von Zellen oder den Abstand zwischen den Zellen scrollen kann, um durch Städte zu scrollen, mag ich eine so schlechte Benutzeroberfläche immer noch nicht. Lass es uns reparieren.
Um Konflikte zu lösen, müssen wir eine
Delegatmethode namens
gestRecognizerShouldBegin (_ :) implementieren. Diese Methode steuert, ob der Gestenerkenner weiterhin Berührungen interpretieren soll. Wenn Sie in der Methode
false zurückgeben , ignoriert der Gestenerkenner Berührungen. Wir geben unserem eigenen Panorama-Erkennungswerkzeug die Möglichkeit, horizontale Bewegungen zu ignorieren.
Legen Sie dazu den
Delegaten unseres Pan-Erkenners fest. Fügen Sie die folgende Codezeile in die
panRecognizer- Initialisierung ein (Sie können den Code direkt vor dem
Rückkehrerkenner platzieren :
recognizer.delegate = self
Anschließend implementieren wir die
gestRecognizerShouldBegin (_ :) -Methode wie folgt:
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return abs((panRecognizer.velocity(in: panRecognizer.view)).y) > abs((panRecognizer.velocity(in: panRecognizer.view)).x) }
Wir öffnen / schließen, wenn die vertikale Geschwindigkeit größer als die horizontale Geschwindigkeit ist.
Wow! Lassen Sie uns die Anwendung erneut testen. Jetzt können Sie sich in der Liste der Städte bewegen, indem Sie nach links / rechts über die Zellen streichen.
Bonus: Benutzerdefinierte Synchronisierungsfunktionen
Bevor wir dieses Tutorial beenden, sprechen wir über Timing-Funktionen. Erinnern Sie sich noch an den Fall, als der Entwickler Sie aufforderte, eine benutzerdefinierte Synchronisierungsfunktion für die von Ihnen erstellte Animation zu implementieren?
Normalerweise sollten Sie die
UIView.animation in
CABasicAnimation ändern oder in
CATransaction einschließen .
Mit dem UIViewPropertyAnimator können Sie problemlos benutzerdefinierte Timing-Funktionen implementieren.
Timing-
Funktionen (oder Beschleunigungsfunktionen) werden als Animationsgeschwindigkeitsfunktionen verstanden, die die Änderungsrate der einen oder anderen animierten Eigenschaft beeinflussen. Derzeit werden vier Typen unterstützt: EasyInOut, EasyIn, EasyOut, linear.
Ersetzen Sie die Animator-Initialisierung wie folgt durch diese Timing-Funktionen (versuchen Sie, Ihre eigene kubische Bezier-Kurve zu zeichnen):
private lazy var animator: UIViewPropertyAnimator = { let cubicTiming = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.17, y: 0.67), controlPoint2: CGPoint(x: 0.76, y: 1.0)) return UIViewPropertyAnimator(duration: 0.3, timingParameters: cubicTiming) }()
Anstatt kubische Synchronisationsparameter zu verwenden, können Sie alternativ auch die Federsynchronisation verwenden, zum Beispiel:
let springTiming = UISpringTimingParameters(mass: 1.0, stiffness: 2.0, damping: 0.2, initialVelocity: .zero)
Versuchen Sie, das Projekt erneut zu starten und zu sehen, was passiert.
Fazit
Mit UIViewPropertyAnimator können Sie statische Bildschirme und Benutzerinteraktionen durch interaktive Animationen verbessern.
Ich weiß, dass Sie es kaum erwarten können, zu realisieren, was Sie in Ihrem eigenen Projekt gelernt haben. Wenn Sie diesen Ansatz in Ihrem Projekt anwenden, wird es sehr cool sein. Lassen Sie mich dies wissen, indem Sie unten einen Kommentar hinterlassen.
Als Referenz können Sie hier
den endgültigen Entwurf herunterladen .
Weitere Links
Professionelle Animationen mit UIKit -
https://developer.apple.com/videos/play/wwdc2017/230/UIViewPropertyAnimator-Dokumentation für Apple-Entwickler -
https://developer.apple.com/documentation/uikit/uiviewpropertyanimator