Die List in Swift UI - Teil 7 - Wischgesten


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(headerSectionHeader(headlineText: section)) {

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

        NavigationLink (destinationPersonDetailView(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)

      }

    }

  }

}

Stacks Image 198

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 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: 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")

                } )

        }

    }

}

Stacks Image 202

Geschrieben am: 18.08.2021
Technologien: SwiftUI, List