
Eine häufige Aufgabe in der iOS-Entwicklung sind die erweiterbaren / faltbaren Abschnitte in einer UITableView. Heute realisieren wir diese Aufgabe mit SwiftUI. Als kleine Wendung fügen wir ein animiertes Dreieck in die Abschnittsüberschrift ein und lassen die Zellen ebenfalls expandieren.
Die Entwicklung erfolgte auf Xcode 11.2 für macOS Catalina 10.15.1
Starten Sie das Projekt
Starten Sie Xcode, File - New Project - Single View App. Geben Sie im Dialogfeld die Swift-Entwicklungssprache an. Die Benutzeroberfläche wird mit SwiftUI erstellt.

Daten
Als Demonstrationsdaten werden wir einige lustige geflügelte Ausdrücke in Latein mit ihrer Übersetzung ins Russische verwenden.
Fügen Sie dem Projekt eine neue Swift-Datei hinzu, nennen Sie sie
Data.swift und schreiben Sie dort Folgendes:
struct QuoteDataModel : Identifiable { var id: String { return latin } var latin : String var russian : String var expanded = false } struct SectionDataModel : Identifiable { var id: Character { return letter } var letter : Character var quotes : [QuoteDataModel] var expanded = false }
QuoteDataModel ist ein Modell eines einzelnen Ausdrucks und wird in Zukunft zum Inhalt jeder einzelnen Zelle. Darin speichern wir den Originaltext des Ausdrucks, seine Übersetzung und das Vorzeichen der "erweiterten" Zelle (standardmäßig ist es "minimiert")
SectionDataModel ist ein Modell jedes einzelnen Abschnitts. Hier speichern wir den "Buchstaben" des Abschnitts, eine Reihe von Anführungszeichen, die mit diesem Buchstaben beginnen, sowie ein Zeichen für den "erweiterten" Abschnitt (standardmäßig ist er auch "reduziert").
Zukünftig werden wir all dies in einer Listenansicht anzeigen, die voraussetzt, dass die Daten dafür mit dem
Identifizierbaren Protokoll
übereinstimmen . Dazu definieren wir die Eigenschaft
id , die für jedes Element in der Liste eindeutig sein muss.
Außerdem bilden wir in derselben Datei Data.swift unsere Daten:
var latinities : [SectionDataModel] = [ SectionDataModel(letter: "C", quotes: [ QuoteDataModel(latin: "Calvitium non est vitium, sed prudentiae indicium.", russian: " , ."), QuoteDataModel(latin: "Conjecturalem artem esse medicinam.", russian: " ."), QuoteDataModel(latin: "Crede firmiter et pecca fortiter!", russian: " !")]), SectionDataModel(letter: "H", quotes: [ QuoteDataModel(latin: "Homo sine religione sicut equus sine freno.", russian: " ."), QuoteDataModel(latin: "Habet et musca splenem.", russian: " .")]), SectionDataModel(letter: "M", quotes: [ QuoteDataModel(latin: "Malum est mulier, sed necessarium malum.", russian: " , ."), QuoteDataModel(latin: "Mulierem ornat silentium.", russian: " .")])]
Beschäftigen wir uns mit der Schnittstelle
Nun legen wir fest, wie die Abschnittsüberschrift und jede Zelle aussehen.
Wählen Sie im Menü Datei - Neu - Datei - SwiftUI-Ansicht.
Nennen Sie die Datei
HeaderView.swift und ersetzen Sie ihren Inhalt durch Folgendes:
import SwiftUI struct HeaderView : View { var section : SectionDataModel var body: some View { HStack() { Spacer() Text(String(section.letter)) .font(.largeTitle) .foregroundColor(Color.black) Spacer() } .background(Color.yellow) } } struct HeaderView_Previews: PreviewProvider { static var previews: some View { HeaderView(section: latinities[0]) } }

Jetzt wieder File - New - File - SwiftUI View.
Benennen Sie die Datei
QuoteView.swift und ersetzen Sie ihren Inhalt durch Folgendes:
import SwiftUI struct QuoteView: View { var quote : QuoteDataModel var body: some View { VStack(alignment: .leading, spacing: 5) { Text(quote.latin) .font(.title) if quote.expanded { Group() { Divider() Text(quote.russian).font(.body) }.transition(.move(edge: .top)).animation(.default) } } } } struct QuoteView_Previews: PreviewProvider { static var previews: some View { QuoteView(quote: latinities[0].quotes[0]) } }

Öffnen Sie nun die Datei ContentView.swift und ändern Sie die Struktur der ContentView wie folgt:
struct ContentView: View { var body: some View { List { ForEach(latinities) { section in Section(header: HeaderView(section: section), footer: EmptyView()) { if section.expanded { ForEach(section.quotes) { quote in QuoteView(quote: quote) } } } } } .listStyle(GroupedListStyle()) } }
Herzlichen Glückwunsch, Sie haben gerade die Liste mit aktuellen Daten gefüllt! Für jedes Element des
Latinities- Arrays erstellen wir einen Abschnitt mit einer auf der
HeaderView basierenden
Kopfzeile und einer leeren Fußzeile. Wenn der Abschnitt „erweitert“ ist, bilden wir für jeden Ausdruck im Anführungszeichen-Array eine Zelle, die auf
QuoteView basiert. In unseren Daten sind alle Abschnitte und alle Zellen „reduziert“. Wenn Sie also Canvas sichtbar machen, werden nur Abschnittsüberschriften angezeigt:

Wie Sie verstehen, ist die Anwendung jetzt vollständig "tot" und noch weit von unserem endgültigen Ziel entfernt. Aber bald werden wir es reparieren!
Ändern Sie die Abschnittsüberschrift leicht
Zurück zur HeaderView.swift-Datei. Fügen Sie in der HeaderView-Struktur unmittelbar nach dem Text Folgendes hinzu:
struct Triangle : Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: 0, y: rect.height - 1)) path.addLine(to: CGPoint(x: sqrt(3)*(rect.height)/2, y: rect.height/2)) path.closeSubpath() return path } }
Diese Struktur liefert ein gleichseitiges Dreieck. Fügen Sie nun unser Dreieck in die Kopfzeile ein.
Fügen Sie im
HStack vor dem ersten
Spacer Folgendes hinzu:
Triangle() .fill(Color.black) .overlay( Triangle() .stroke(Color.red, lineWidth: 5) ) .frame(width : 50, height : 50) .padding() .rotationEffect(.degrees(section.expanded ? 90 : 0), anchor: .init(x: 0.5, y: 0.5)).animation(.default))

Daten ändern
Zurück zu unseren Daten. Öffnen Sie Data.swift und binden
Sie unser
Latinities- Array in eine neue UserData-Klasse ein.
class UserData : ObservableObject { @Published var latinities : [SectionDataModel] = [ SectionDataModel(letter: "C", quotes: [ QuoteDataModel(latin: "Calvitium non est vitium, sed prudentiae indicium.", russian: " , ."), QuoteDataModel(latin: "Conjecturalem artem esse medicinam.", russian: " ."), QuoteDataModel(latin: "Crede firmiter et pecca fortiter!", russian: " !")]), SectionDataModel(letter: "H", quotes: [ QuoteDataModel(latin: "Homo sine religione sicut equus sine freno.", russian: " ."), QuoteDataModel(latin: "Habet et musca splenem.", russian: " .")]), SectionDataModel(letter: "M", quotes: [ QuoteDataModel(latin: "Malum est mulier, sed necessarium malum.", russian: " , ."), QuoteDataModel(latin: "Mulierem ornat silentium.", russian: " .")])] }
Denken Sie daran, Latinities auch als
@Published zu kennzeichnen .
Was haben wir getanObservableObject ist ein spezielles Objekt für unsere Daten, das an bestimmte Ansichten „gebunden“ werden kann. SwiftUI "überwacht" alle Änderungen, die sich auf die Ansicht auswirken können, und ändert nach Änderung der Daten die Ansicht.
Nach dem "Wrapping" von Latinities hatten wir viele Fehler, wir werden sie korrigieren. Öffnen Sie
HeaderView.swift und korrigieren Sie die Struktur von
HeaderView_Previews wie folgt:
struct HeaderView_Previews: PreviewProvider { static var previews: some View { HeaderView(section: UserData().latinities[0]) } }
Nehmen Sie nun ähnliche Änderungen an
QuoteView.swift vor :
struct QuoteView_Previews: PreviewProvider { static var previews: some View { QuoteView(quote: UserData().latinities[0].quotes[0]) } }
Öffnen Sie die Datei ContentView.swift und fügen Sie diese vor der
Body- Deklaration hinzu
@ObservedObject var userData = UserData()
Beleben Sie die Landschaft wieder
Zurück zur Datei ContentView.swift. Fügen Sie in der ContenView-Struktur unmittelbar nach der userData-Definition zwei Funktionen hinzu:
func sectionIndex(section : SectionDataModel) -> Int { userData.latinities.firstIndex(where: {$0.letter == section.letter})! } func quoteIndex(section : Int, quote : QuoteDataModel) -> Int { return userData.latinities[section].quotes.firstIndex(where: {$0.latin == quote.latin})! }
Fügen
Sie der Abschnittsüberschrift und der Zelle, die Sie
erstellen, die Modifizierer
onTapGesture hinzu . Endgültige Ansicht des
Körperinhalts :
var body: some View { List { ForEach(userData.latinities) { section in Section(header: HeaderView(section: section) .onTapGesture { self.userData.latinities[self.sectionIndex(section: section)].expanded.toggle() }, footer: EmptyView()) { if section.expanded { ForEach(section.quotes) { quote in QuoteView(quote: quote) .onTapGesture { let sectionIndex = self.sectionIndex(section: section) let quoteIndex = self.quoteIndex(section: sectionIndex, quote: quote) self.userData.latinities[sectionIndex].quotes[quoteIndex].expanded.toggle() } } } } } } .listStyle(GroupedListStyle()) }
Die
Funktionen sectionIndex und
quoteUndex geben den Index der Abschnitte und Ausdrücke zurück, die an sie übergeben wurden. Nachdem wir diese Indizes erhalten haben, ändern wir die Werte der
erweiterten Eigenschaften in unserem
Latinitätsfeld , was zur Faltung / Entfaltung des Abschnitts oder Ausdrucks führt.

Fazit
Das fertige Projekt kann hier
heruntergeladen werden .
Einige nützliche Links:
Ich hoffe, dass Ihnen die Veröffentlichung weiterhilft!