Im Juni hörten
wir zum ersten Mal von
SwiftUI - einer völlig neuen Methode zum Erstellen und Arbeiten mit UI-Elementen in iOS- und macOS-Apps (auch iPadOS). Es fühlte sich an wie Weihnachten im Sommer. Es ist neu, deklarativ, sexy! Und jetzt, nur wenige Wochen nach der Veröffentlichung von iOS 13, können wir SwiftUI in all unseren Projekten einsetzen. Lassen Sie uns lernen, wie wir dieses erstaunliche Tool, das Apple uns gegeben hat, verwenden, um die klassischen zunderartigen Swipe-Karten zu erstellen.
In diesem Artikel möchte ich Ihnen zeigen, wie Sie mit nur wenigen Codezeilen eine zunderähnliche Kartenansicht und ein zunderähnliches Verhalten erzielen (Swipe to Action).
Um dies zu erreichen, müssen wir die folgenden Schritte ausführen:
- Erstellen Sie UserView
- Navigationsansicht erstellen
- Erstellen Sie BottomBarView
- Swipeview erstellen
- Fügen Sie all dies in ContentView zusammen
Also fangen wir an.
Userview
UserView besteht aus zwei Unteransichten, eine ist
NameView, die den Benutzernamen, das Alter und die Hobbys enthält, und die zweite Ansicht ist nur eine Avataransicht, in der das Profilbild des Benutzers angezeigt wird.
struct NameView: View { let name: String let age: Int let hobby: String var body: some View { VStack(alignment: .leading) { Spacer() Text("\(name), \(age)") .font(.title) .fontWeight(.semibold) .foregroundColor(.white) Text(hobby) .font(.system(size: 16)) .fontWeight(.regular) .foregroundColor(.white) } .padding() } }
Zuerst müssen wir die
NameView definieren, diese repräsentiert den Benutzernamen, das Alter und das Hobby.
NameView entspricht dem
View- Protokoll, mit dem benutzerdefinierte Ansichten in SwiftUI definiert werden. Das Ansichtsprotokoll hat nur eine Anforderung und definiert die Körpereigenschaft, die die Ansichtsstruktur zurückgeben und deren Verhalten beschreiben soll. Weitere Informationen zum
View- Protokoll finden Sie in der offiziellen
Apple-Dokumentation .
Lassen Sie uns die Objekte aufschlüsseln, mit denen wir diese
Ansicht definieren:
- VStack, der wie ein Container für alle Objekte wirkt, die sie vertikal ausrichten
- Spacer , der SwiftUI mitteilt, dass diese Ansicht unten ausgerichtet werden soll
- Text, der die Bezeichnung mit Name und Alter mit den folgenden Eigenschaften darstellt:
- Zweites Textobjekt, das ähnliche Eigenschaften aufweist und das Hobby des Benutzers anzeigt
Bitte beachten Sie, dass wir hier keine return-Anweisung innerhalb der body-Eigenschaft verwenden, sondern stattdessen einen VStack zurückgeben. SwiftUI verwendet den in Swift 5.0 implementierten Vorschlag zum Weglassen der Rückgabe. Mehr dazu können Sie
hier nachlesen .
Avataransicht
So wird
AvatarView definiert:
struct AvatarView: View { let image: UIImage var body: some View { Image(uiImage: image) .resizable() .overlay( Rectangle() .fill(LinearGradient(gradient: Gradient(colors: [.clear, .black]), startPoint: .center, endPoint: .bottom)) .clipped() ) .cornerRadius(12.0) } }
Kommen wir zu den Komponenten, aus denen diese Avatar-Einheit besteht:
- Bild - Zeigt das Bild des Benutzers an
- Größenänderung - Diese Methode gibt an, dass die Größe des Bildes an die Stelle angepasst werden soll, an der es eingebettet ist
- Overlay (Rechteck) - hier definieren wir einen Farbverlauf, der ein schöner Hintergrund für NameView ist. Dieser Farbverlauf beginnt in der Bildmitte und endet unten. Er hat am Anfang eine klare Farbe und unten eine schwarze Farbe
- cornerRadius - das Bild hat einen Eckenradius
Lassen Sie uns nun diese beiden Ansichten in eine einzelne Containeransicht mit dem Namen
UserView einbetten .
Userview
struct UserView: View { let userModel: UserModel var body: some View { ZStack(alignment: .leading) { AvatarView(image: userModel.image) NameView(name: userModel.name, age: userModel.age, hobby: userModel.hobby) } .shadow(radius: 12.0) .cornerRadius(12.0) } }
Folgendes ist los:
- ZStack - Dies ist eine Stapelansicht, bei der die untergeordneten Elemente auf derselben Achse ausgerichtet werden. Mehr über ZStack erfahren Sie hier
- AvatarView - Unsere Avatar-Ansicht mit dem über UserModel bereitgestellten Bild
- Namensansicht - Unsere Namensansicht zeigt den Namen basierend auf dem Benutzermodell an
Führen Sie nach all diesen Schritten die App aus. Sie erhalten folgenden Bildschirm:
Fügen wir jetzt eine kleine Hilfsmethode hinzu. Bevor ich Ihnen zeige, wie NavigationView definiert ist, erstellen wir eine Hilfsmethode, die wie folgt aussieht:
struct ViewFactory { static func button(_ name: String, renderingMode: Image.TemplateRenderingMode = .original) -> some View { Button(action: {}) { Image(name) .renderingMode(renderingMode) } } }
Hier haben wir eine Button Factory-Methode definiert, die aus einem bestimmten Bild und einem Rendermodus einen neuen Button erstellt. Es gibt keinen Action-Handler, da dies für diesen Artikel nicht relevant ist.
Navigationsansicht
struct NavigationView: View { var body: some View { HStack { ViewFactory.button("profile_icon") Spacer() ViewFactory.button("fire_icon") .scaleEffect(2) Spacer() ViewFactory.button("chat_icon") } } }
SwiftUI stellt die
Spacer automatisch auf die gleiche Breite ein und gibt uns die folgende Navigationsansicht:
Bottombarview
struct BottomBarView: View { var body: some View { HStack { ViewFactory.button("back_icon", renderingMode: .template) .foregroundColor(.orange) .background( GeometryReader { geometry in Circle() .offset(x: 2.5) .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 1.5, height: geometry.size.height * 1.5) } ) Spacer() ... }
Im obigen Codeausschnitt haben wir die erste Schaltfläche in unserer Leistenansicht definiert. Folgendes ist los:
- ViewFactory.button - hier verwenden wir unsere Hilfsmethode, um einen Button mit einem Bild mit renderingMode .template zu definieren, mit dem Sie eine benutzerdefinierte Farbe für dieses Bild festlegen können
- .foregroundColor - Definiert die Farbe unserer Ansicht
- .background - Diese Methode definiert die Hintergrundansicht des angegebenen Objekts
- GeometryReader - eine Containeransicht, die ihren Inhalt als Funktion ihrer eigenen Größe und ihres eigenen Koordinatenraums definiert. Wir verwenden dies, um die aktuelle Größe einer Schaltfläche abzurufen und den Hintergrundkreis mit dem angegebenen Rahmen zu definieren. Erfahren Sie hier mehr über Geometrieleser.
- Kreis - Definiert die Hintergrundform
- .offset - Versatz der x-Achse des Kreises
- .foregroundColor - Kreistönungsfarbe
- .shadow - Kreisschatten
- .frame - Definiert den Kreisrahmen anhand der Größe des Geometrielesers (hier definieren wir einen Hintergrundkreis, der 1,5x größer als die aktuelle Schaltfläche ist).
Lassen Sie uns nun die restlichen Schaltflächen implementieren:
struct BottomBarView: View { var body: some View { HStack { ViewFactory.button("back_icon", renderingMode: .template) .foregroundColor(.orange) .background( GeometryReader { geometry in Circle() .offset(x: 2.5) .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 1.5, height: geometry.size.height * 1.5) } ) Spacer() ViewFactory.button("close_icon", renderingMode: .template) .foregroundColor(.red) .background( GeometryReader { geometry in Circle().foregroundColor(.white) .frame(width: geometry.size.width * 2, height: geometry.size.height * 2) .shadow(color: .gray, radius: 12) } ) Spacer() ViewFactory.button("approve_icon", renderingMode: .template) .foregroundColor(.green) .background( GeometryReader { geometry in Circle() .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 2, height: geometry.size.height * 2) } ) Spacer() ViewFactory.button("boost_icon", renderingMode: .template) .foregroundColor(.purple) .background( GeometryReader { geometry in Circle() .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 1.5, height: geometry.size.height * 1.5) } ) } .padding([.leading, .trailing]) } }
Und als Ergebnis haben wir jetzt diese schöne Aussicht:
Swipeview
Dieser Abschnitt ist für fortgeschrittene SwiftUI. Hier wird es wirklich interessant. Wir möchten die Wischgeste in der Aktionsansicht implementieren. Dieses Verhalten ist ein nützlicher Anwendungsfall für einen PageViewController, aber dieser View-Controller wird bald in der Vergangenheit sein, sodass wir hier die wahre Leistungsfähigkeit von SwiftUI zeigen können.
Schauen wir uns also an, wie SwipeView implementiert ist:
struct SwipeView: View { @State private var offset: CGFloat = 0 @State private var index = 0 let users = [...] let spacing: CGFloat = 10 var body: some View { GeometryReader { geometry in return ScrollView(.horizontal, showsIndicators: true) { HStack(spacing: self.spacing) { ForEach(self.users) { user in UserView(userModel: user) .frame(width: geometry.size.width) } } } .content.offset(x: self.offset) .frame(width: geometry.size.width, alignment: .leading) .gesture( DragGesture() .onChanged({ value in self.offset = value.translation.width - geometry.size.width * CGFloat(self.index) }) .onEnded({ value in if -value.predictedEndTranslation.width > geometry.size.width / 2, self.index < self.users.count - 1 { self.index += 1 } if value.predictedEndTranslation.width > geometry.size.width / 2, self.index > 0 { self.index -= 1 } withAnimation { self.offset = -(geometry.size.width + self.spacing) * CGFloat(self.index) } }) ) } } }
Hier haben wir einige neue interessante SwiftUI-Konzepte verwendet:
- @ State - Ein beständiger Wert eines bestimmten Typs, über den eine Ansicht den Wert liest und überwacht. Dies bedeutet, dass die Ansicht bei jeder Änderung dieser Eigenschaft neu geladen wird, um sie an die jeweilige Statusaktualisierung anzupassen. Hier können Sie mehr über State erfahren .
- DragGesture - Dieses Objekt wird verwendet, um jeden Schlag zu erkennen, den der Benutzer auf dem Bildschirm macht. Weitere Informationen hierzu finden Sie hier: developer.apple.com/documentation/swiftui/draggesture
- @ State Private Var Offset: CGFloat = 0 - Diese Eigenschaft wird verwendet, um den aktuellen Bildlauf-Ansichts-Offset zu definieren, wenn Benutzer über den Bildschirm wischen
- @ State Private Var Index = 0 - Diese Eigenschaft definiert, welche Benutzeransicht derzeit auf dem Bildschirm angezeigt wird
- ScrollView - horizontale Scroll-Ansicht ohne Indikatoren, die ein Container für unsere Benutzeransicht sein wird
- HStack - horizontale Stapelansicht, die alle Benutzeransichten enthält
- content.offset (self.offset) - Stellt eine Verbindung zwischen dem Versatzstatus und dem Versatz des Inhalts der Bildlaufansicht her. Dies bedeutet, dass bei jeder Änderung der Versatzeigenschaft auch der Versatz der Bildlaufansicht aktualisiert wird
Wir zählen die vorhandenen Benutzer auf, indem
wir für jedes Element eine
UserView erstellen:
- .frame - hier definieren wir den Bildlauf-Ansichtsrahmen, der an die Breite des Bildschirms angepasst und ordnungsgemäß an seinem Container ausgerichtet werden soll
- .gesture - hier fügen wir unser DragGesture- Objekt hinzu
DragGesture ist etwas kompliziert, fügt jedoch die gesamte Paginierungslogik in nur wenigen Codezeilen hinzu. Lassen Sie uns
DragGesture aufschlüsseln :
- onChanged () - Dieser Block wird immer dann aufgerufen , wenn der Benutzer startet, und befindet sich in der Zeit der Dragning-Geste. Hier wird der aktuelle Versatz der Benutzeransicht berechnet, der dem Finger des Benutzers folgt
- onEnded () - hier werden wir informiert, wenn die Ziehbewegung endet, hier müssen wir berechnen, ob der Benutzer diese Ansicht (links oder rechts) streichen möchte oder ob diese Geste markiert wurde und der Benutzer auf diesem Bildschirm bleiben möchte
- withAnimation - Dieser Abschluss wird mit Animation aufgerufen und ermöglicht eine sanfte Änderung des Versatzes
Inhaltsansicht
struct ContentView: View { var body: some View { VStack { NavigationView() .padding(.bottom) SwipeView() .padding(.bottom) BottomBarView() } .padding() } }
Unsere Inhaltsansicht ist zu diesem Zeitpunkt äußerst einfach: Sie setzt alle erstellten Ansichten, die wir zuvor erstellt haben, in einem vertikalen Stapel (
VStack ) zusammen. Für
NavigationView und
SwipeView haben wir am unteren Rand einige Standardabstände hinzugefügt, und für den gesamten
VStack wurden an allen Kanten Abstände hinzugefügt.
Das war's Fertig So sieht unsere App jetzt aus:
Letzte Gedanken
Wie wir sehen können, ist SwiftUI ein sehr leistungsfähiges Tool, mit dem wir die Benutzeroberfläche auf einfache Weise in einem kurzen deklarativen Code definieren und bearbeiten können.
Reagieren Native Entwickler würden dieses deklarative Paradigma sofort erkennen.
Aber denken Sie daran: SwiftUI befindet sich noch in der Entwicklung und kann vorerst äußerst instabil sein. Wenn Sie die gesamte Codebasis für dieses Projekt überprüfen möchten, finden Sie sie auf
Github .
Wenn Sie irgendwelche Gedanken oder Fragen zu SwiftUI haben, teilen Sie diese gerne in Kommentaren mit. Wenn dir dieser Artikel gefallen hat, teile ihn bitte mit deiner Community, damit wir ihn weiter verbreiten können!