Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IndexSet referring to index of the section instead of the row

I want to implement a swipe-to-delete functionality on a SwiftUI Section by using the .onDelete modifier. The problem is that it always deletes the first item in the list.

My view has a list with dynamic sections created with a ForEach.

struct SetListView : View {

    var setlist: Setlist
    var body : some View {

        List { 
            ForEach(setlist.sets) { 
                 SetSection(number: $0.id, songs: $0.songs) 
            }
        }
        .listStyle(.grouped)
    }
}

In each section, there is another ForEach to create the dynamic rows:

private struct SetSection : View {

    var number: Int
    @State var songs: [Song]

    var body : some View {
        Section (header: Text("Set \(number)"), footer: Spacer()) {
            ForEach(songs) { song in
                SongRow(song: song)
            }
            .onDelete { index in
                self.songs.remove(at: index.first!)
            }
        }
    }
}

While debugging, I found out that the IndexSet is referring to the current section instead of the row. So when deleting items from the first section, always the first item gets deleted (as the index for the first section is 0).

Is this a bug in SwiftUI?

If not, then how could I get the index for the row?

like image 742
KrohnicDev Avatar asked Jun 14 '19 19:06

KrohnicDev


1 Answers

It seems to work fine on Xcode 12.5

I'm using it like this:

struct Sections: View {
    var items: [SomeData]
    private var sections: [Date: [SomeData]] {
        Dictionary(grouping: items, by: { $0.date })
    }
    private var headers: [Date] {
        sections.map({ $0.key }).sorted().reversed()
    }

    var body: some View {
        List {
            ForEach(headers, id: \.self) { date in
                Section(header: Text(date.friendly) {
                    AList(items: sections[date]!)
                }
            }
        }
    }
}

struct AList: View {
    var items: [SomeData]
    var body: some View {
        ForEach(items) { data in
            ...
        }
        .onDelete(perform: delete)
    }

    private func delete(at offsets: IndexSet) {
        // You can use `items.remove(atOffsets: offsets)`
        for offset in offsets {
            let data = items[offset]
            print("\(data)") 
    // You can check here that this is the item that you want to remove and then you need to remove it from your data source. 
    // I'm using Realm and @Published vars that works fine, you should adapt to your logic.
        }
    }
}
like image 101
Kitos Avatar answered Sep 26 '22 01:09

Kitos