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(), at: 0)
}
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 ContentView: View {
@ObservedObject var repo = PersonsRepository(randomPersonsCount: 10)
init() {
let customAppearance = UINavigationBarAppearance()
// Backgroundcolor
customAppearance.backgroundColor = UIColor.lightGray
// Font color for navigationBarTitleDisplayMode large
customAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
// Font color for navigationBarTitleDisplayMode inline
customAppearance.titleTextAttributes = [.foregroundColor: UIColor.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")
} )
}
}
}
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 SectionHeader: View
{
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(top: 0, leading: 0, bottom: 0, trailing: 0))
.background(backColor)
}
}
In der List muss dann nur noch die Zeile
Section(header: Text(section)) {
gegen die Zeile
Section(header: SectionHeader(headlineText: section)) {
ausgetauscht werden und Sektionsüberschriften erhalten ein komplett neues Erscheinungsbild.
Geschrieben am: 18.08.2021 Technologien: SwiftUI, List