Eine weitere Neuerung in iOS15 ist eine Funktion, um Listen durch ziehen vom oberen Bildschirmrand zu aktualisieren. Allerdings liefert Apple hier nicht wirklich den Mechanismus, um den Inhalt der Liste neu aufzubauen, sondern lediglich die passende Animation und eine Closure. Trotzdem ist es eine immense Arbeitserleichterung. In der Vergangenheit haben Entwickler diese Pull-to-Refresh-Funktion selbst implementiert. Jetzt genügt ein einzelner View-Modifier. Es ist refreshable, der an die List angehängt werden muss. Im aktuellen Projekt gibt es keine Notwendigkeit, die List zu aktualisieren, denn der Inhalt ändert sich nicht. Um den View-Modifier auszuprobieren, soll deshalb ein Mechanismus implementiert werden, der bei Pull-to-Refresh eine weitere zufällige Person der Liste hinzufügt. Nur wenige Anweisungen sind nötig.
.refreshable {
self.repo.addRandomPerson()
}
Das komplette Listing des ContentView
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: SectionHeader(headlineText: section)) {
ForEach(self.repo.persons.filter {$0.company == section} , id: \.id) { person in
NavigationLink (destination: PersonDetailView(person: person))
{
PersonListCell(person: person)
.badge(Text(person.getAge())
.foregroundColor(Color.blue)
.font(.subheadline )
)
}
.swipeActions(edge: .leading , allowsFullSwipe: true) {
Button {
print("Sent Button")
} label: {
Label("Send", systemImage: "paperplane.fill")
}
.tint(.indigo)
Button {
print("Bookmark Button")
} label: {
Label("Bookmark", systemImage: "bookmark.circle")
}
.tint(.teal)
}
.swipeActions(edge: .trailing , allowsFullSwipe: true) {
Button {
withAnimation {
self.repo.delete(person: person)
}
} label: {
Label("Delete", systemImage: "trash.fill")
}
.tint(.red)
}
}
}
}
.listRowSeparatorTint( Color(red: 0.2, green: 0, blue: 1.0, opacity: 0.5) )
}.listStyle(PlainListStyle())
.refreshable {
self.repo.addRandomPerson()
}
.navigationTitle("List and People")
.navigationBarTitleDisplayMode(.large)
.navigationBarItems(trailing:
Button(action: {
self.repo.addRandomPerson()
}) {
Image(systemName: "plus.circle.fill")
} )
}
}
}
Die Anweisungen, die bei einem Pull-to-Refresh ausgeführt werden, können durchaus etwas dauern. Werden beispielsweise Daten aus dem Internet nachgeladen, passiert das nicht umgehend. Es ist daher zu empfehlen, den Code dort asynchron auszuführen. Mit async und await ist es glücklicherweise nicht schwer. Leider ist es nicht möglich, die Funktion addRandomPerson in eine Funktion umzuwandeln, die asynchon ausgeführt wird. Weil sie ebenfalls durch den Button in der Naviagtionsleiste aufgerufen wird, muss sie synchron sein. Es spricht aber nichts dagegen, die Klasse PersonsRepository um eine weitere Methode zu erweitern, die dann addRandomPerson aufruft. So eine Methode könnte beispielsweise addRandomPersonAsync heißen.
func addRandomPersonAsync() async {
self.addRandomPerson()
}
func addRandomPerson() {
self.persons.insert(RandomPersonGenerator.shared.generate(), at: 0)
}
In der View muss dann noch nur die asynchrone Methode aufgerufen werden.
.refreshable {
await self.repo.addRandomPersonAsync()
}
Zugegeben, zur Demonstration der Befehle async und await ist es ein schlechtes Beispiel, weil die Ausführung nicht wirklich lange dauert. Aber so könnte es funktionieren.
Geschrieben am: 29.08.2021 Technologien: SwiftUI, List