Viele iOS-Apps verwenden Wischgesten, um die Elementen in einer List mit zusätzlichen Funktionen zu verbinden. Wischt ein Nutzer über einen Eintrag, bewegt sich das Element zur Seite und es erscheinen Schaltflächen. In der Vergangenheit war die Programmierung dieser Buttons eine Herausforderung. Seit der Einfügung der swipeActions-Modifier in iOS15 ist es einfacher. Jedoch gibt es einen Umstand, den man beachten muss: Befindet sich das Listenelement in einem NavigationLink, müssen die swipeActions diesen Link angehängt werden. Sie dem, im NavigationLink eingebettetem, Steuerelement anzuhängen, funktioniert nicht.
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)
}
.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)
}
}
}
}
Im gezeigtem Listing werden dem Listenelement mit Hilfe der swipeActions zwei Buttons angehängt. Der erste Button in der Farbe Indigo, der zweite Button in der Farbe Teal. Der Parameter edge: .leading bewirkt, dass der Button auf der linken Seite des Listenelementes angezeigt werden. Es ist eine Wischgeste von links nach rechts nötig, um die Schaltflächen einzublenden. Der Parameter allowsFullSwipe: true hat zur Folge, dass die Aktion des Button auch ausgeführt wird, wenn die Wischgeste über die komplette Breite der List ausgeführt wird. Es ist dann nicht erforderlich, explizit auf den Button zu tippen. Gibt es unter den swipeActions mehr als einen Button, gilt allowsFullSwipe nur für die erste Schaltfläche. Oft wird in Apps eine Wischgeste von rechts nach links implementiert, um Listenelemente zu löschen. Das folgende Listing zeigt beispielhaft, wie so ein Button konfiguriert werden könnte. Sollen einen Listenelement Wischgesten von beiden Richtungen zugewiesen werden, muss dem NavigationLink lediglich ein zweiter swipeActions-Modifier angehängt werden. Ein Mal mit edge: .leading und ein weitere mit edge: .trailing.
.swipeActions(edge: .trailing , allowsFullSwipe: true) {
Button {
print("Delete Button")
} label: {
Label("Delete", systemImage: "trash.fill")
}
.tint(.red)
}
Um einen Listeneintrag tatsächlich zu löschen, muss das zugrundeliegende Datenmodel gelöscht werden. In dieser App sind das die Instanzen der Klasse Person, die im PersonsRepository verwaltet werden. Eine gute Umsetzung ist, das PersonsRepository um eine Funktion zu erweitern, der eine zu löschende Person übergeben werden kann. Das Repository hat dann nur noch die Aufgabe, das korrekte Objekt in seinem Array zu finden und zu entfernen. Glücklicherweise können alle Instanzen von Person über die id eindeutig identifiziert werden.
func delete(person : Person) {
self.persons.removeAll(where: {$0.id == person.id } )
}
Wird die Instanz aus dem Repository entfernt, aktualisiert sich auch die View automatisch. Erneut ist das zurückzuführen auf die Verbindung von ObservableObject und ObservedObject. Falls der Aufruf der delete-Methode in ein withAnimation eingesetzt wird, verschwindet der Listeneintrag sogar mit einer Animation.
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)
}
.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)
}
}
}
}
}.listStyle(PlainListStyle())
.navigationTitle("List and People")
.navigationBarTitleDisplayMode(.large)
.navigationBarItems(trailing:
Button(action: {
self.repo.addRandomPerson()
}) {
Image(systemName: "plus.circle.fill")
} )
}
}
}
Geschrieben am: 18.08.2021 Technologien: SwiftUI, List