Die List in Swift UI - Teil 6 - Sektionen


In diesem Tutorial werden wir uns ansehen, wie der Inhalt der List in verschiedene Sektionen gruppiert werden kann. Das soll in Abhängigkeit der Firma passieren, bei der eine Person beschäftig ist. Diese Information gibt es bisher nicht. Im ersten Schritt muss die Klasse Person deshalb um eine Eigenschaft erweitert werden, in welcher der Arbeitgeber für die Person abgelegt wird. Die Eigenschaft soll company heißen und ist vom Typ string.

import Foundation


public class Person {

    let id = UUID()

    var firstName : String = ""

    var lastName : String = ""

    var birthday : Date = Date()

    var company : String = ""

}

Der zweite Schritt ist die Erweiterung der Klasse RandomPersonGenerator. Sie erhält ein String-Array mit dem Namen companies, welches mit allen zur Verfügung stehenden Arbeitgebern gefüllt wird. Das passiert in der init-Funktion. In der generate-Funktion muss dann lediglich ein zufälliges Element dieses Arrays der Person zugewiesen werden. Das funktioniert erneut mit randomElement. Genau so wie zuvor bei den Vornamen und Nachnamen.

public class RandomPersonGenerator {

    

    static let shared = RandomPersonGenerator()

    private var firstNames = [String]()

    private var lastNames = [String]()

    private var companies = [String]()

    private let minBirthYear = 1960

    private let maxBirthYear = 2010

    

    private init()

    {

        firstNames.append(contentsOf: ["Rose""Billie""Ben""Ken""Michael""Thomas""Mike""Kenzie"])

        firstNames.append(contentsOf: ["Jane""John""Russel""George""Kagney""Arnold"])

        firstNames.append(contentsOf: ["Bruce""Steve""Patty""Bill""Ted""Peter""David""May""Gwen"])

        lastNames.append(contentsOf: ["Swift""Piper""Steele""Rogers""Stark""Hawkins""Lee""Kirby"] )

        lastNames.append(contentsOf: ["Cambell""Hart""Jordan""Fielding""Crispin","Ford""Jones"])

        lastNames.append(contentsOf: ["Anderson""Reeves""Sagan""Morgan""Parker""Watson"])

        

        companies.append(contentsOf: ["Apple""Microsoft""Dell"])

    }

    

    private func getRandomDate() -> Date {

        

        var minComponents = DateComponents()

        minComponents.year = self.minBirthYear

        minComponents.month = 1

        minComponents.day = 1

        let minDate = Calendar.current.date(from: minComponents)


        var maxComponents = DateComponents()

        maxComponents.year = self.maxBirthYear

        maxComponents.month = 12

        maxComponents.day = 31

        let maxDate = Calendar.current.date(from: maxComponents)

        

        let span = TimeInterval.random(in: minDate!.timeIntervalSinceNow...maxDate!.timeIntervalSinceNow)

        return Date(timeIntervalSinceNow: span)

    }

            

    public func generate() -> Person {

        let person = Person()

        person.firstName = self.firstNames.randomElement()!

        person.lastName = self.lastNames.randomElement()!

        person.company = self.companies.randomElement()!

        person.birthday = self.getRandomDate()

        return person

    }    

}

Der nächste Entwicklungsschritt führt in die Klasse PersonsRepository. Dort wird eine Methode benötigt, welche die Arbeitgeber der Personen als ein Array zurückgibt. Sie soll getSections heißen. Sehr einfach können die Firmen mit einer Schleife ermittelt werden, indem jede Person untersucht wird. Ist die Firma der Person bisher nicht bekannt, wird sie einem Array hinzugefügt. Das gefüllte Array wird am Ende durch die Funktion sorted sortiert. Wie viele verschiedene Einträge das Array enthält, wird erst zur Laufzeit ermittelt. Weil die Zuordnung der Personen zu den Firmen zufällig passiert, ist es durchaus möglich, dass alle Personen den gleichen Arbeitgeber bekommen.

import SwiftUI


public class PersonsRepository : ObservableObject  {


    @Published var persons = [Person]()

    

    init(randomPersonsCount : Int) {

                

        for _ in 0..<randomPersonsCount {

            self.persons.append(RandomPersonGenerator.shared.generate())

        }        

    }


    func addRandomPerson() {

        self.persons.insert(RandomPersonGenerator.shared.generate(), at0)

    }

    

    func getSections() -> [String] {

        

        var sections  = [String]()

    

        for person in self.persons {

            if !sections.contains(person.company) {

                sections.append(person.company)

            }

        }

        

        return sections.sorted()

    }

}

In der View sind anschließend größere Änderungen beim Füllen der List erforderlich. Zunächst wird eine ForEach-Schleife benötigt, die über alle Sektionen läuft und dazu passende Sektionen vom Typ Section erzeugt. Die Liste dieser Sektionen liefert die Funktion getSections des PersonsRepository. Die jeder Sektion untergeordneten Personen liefert eine weitere ForEach-Schleife. Sie läuft über das persons-Array des Repository. Jedoch wird hier der Listeninhalt zuvor gefiltert. Immer in Abhängigkeit der aktuellen Sektion, die dem Firmennamen entspricht.

import SwiftUI


struct ContentViewView {

    

    @ObservedObject var repo = PersonsRepository(randomPersonsCount10)

    

    init() {

        let customAppearance = UINavigationBarAppearance()

        // Backgroundcolor

        customAppearance.backgroundColor = UIColor.lightGray

        // Font color for navigationBarTitleDisplayMode large

        customAppearance.largeTitleTextAttributes = [.foregroundColorUIColor.white]

        // Font color for navigationBarTitleDisplayMode inline

        customAppearance.titleTextAttributes = [.foregroundColorUIColor.white]

        

        UINavigationBar.appearance().standardAppearance = customAppearance

        UINavigationBar.appearance().compactAppearance = customAppearance

        UINavigationBar.appearance().scrollEdgeAppearance = customAppearance

    }

    

    var body: some View {

        

        NavigationView {

            List {

                ForEach (self.repo.getSections(), id:\.self ) { section in

                    Section(header: Text(section)) {

                        ForEach(self.repo.persons.filter {$0.company == section}  , id: \.id) { person in

                            NavigationLink (destination: PersonDetailView(person: person))

                            {

                                PersonListCell(person: person)

                            }

                        }

                    }

                }

            }.listStyle(PlainListStyle())

                .navigationTitle("List and People")

                .navigationBarTitleDisplayMode(.large)

                .navigationBarItems(trailing:

                                        Button(action: {

                    self.repo.addRandomPerson()

                }) {

                    Image(systemName: "plus.circle.fill")

                } )

        }

    }

}

Stacks Image 203

Im Beispiel ist der Header jeder Section ein Text-Steuerelement mit dem Namen der Sektion, also dem Firmennamen. Dieser wird über den der Firma zugehörigen Personen angezeigt. Als Header kommt jedoch jedes beliebige Element in Frage, solange es von dem Typ View abgeleitet ist. Benutzerdefinierte Überschriften sind somit kein Problem und der eigenen Kreativität dabei kaum Grenzen gesetzt. Ein Beispiel zeigt der folgende Code mit der Struktur SectionHeader.

import SwiftUI


struct SectionHeaderView

{

    var headlineText : String

    let backColor = Color.white

    let foreColor = Color.blue

    

    var body: some View {

            HStack {

                Text(headlineText)

                    .font(.largeTitle)

                    .foregroundColor(foreColor)

                    .padding(EdgeInsets(top: 0, leading: 15, bottom: 5, trailing: 0))

                Spacer()

            }

        .listRowInsets(EdgeInsets(top0leading0bottom0trailing0))

        .background(backColor)

    }

}

In der List muss dann nur noch die Zeile

Section(header: Text(section)) {

gegen die Zeile

Section(headerSectionHeader(headlineText: section)) {

ausgetauscht werden und Sektionsüberschriften erhalten ein komplett neues Erscheinungsbild.

Stacks Image 199

Geschrieben am: 18.08.2021
Technologien: SwiftUI, List