我想通过使用 .onDelete
修饰符在 SwiftUI Section
上实现滑动删除功能。问题是它总是删除列表中的第一项。
我的 View 有一个列表,其中包含使用 ForEach
创建的动态部分。
struct SetListView : View {
var setlist: Setlist
var body : some View {
List {
ForEach(setlist.sets) {
SetSection(number: $0.id, songs: $0.songs)
}
}
.listStyle(.grouped)
}
}
在每个部分中,还有另一个 ForEach
来创建动态行:
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!)
}
}
}
}
在调试时,我发现 IndexSet
指的是当前部分而不是行。因此,当从第一部分中删除项目时,总是会删除第一个项目(因为第一部分的索引为 0)。
这是 SwiftUI 中的错误吗?
如果不是,那我如何获取该行的索引?
最佳答案
简单来说,这个问题的解决方案是通过以下方式将部分传递给您的删除方法:
- 对您的源数据采用
RandomAccessCollection
。 - 在外部
ForEach
中绑定(bind)该部分,然后在内部ForEach
中使用它,将其传递给删除方法:
List {
ForEach(someGroups.indices) { section in
bind(self.someGroups[section]) { someGroup in
Section(header: Text(someGroup.displayName)) {
ForEach(someGroup.numbers) { number in
Text("\(number)")
}
.onDelete { self.delete(at: $0, in: section) }
}
}
}
}
func delete(at offsets: IndexSet, in section: Int) {
print("\(section), \(offsets.first!)")
}
一个完整的、人为设计的工作示例
(Also available in Gist form for convenience):
import SwiftUI
func bind<Value, Answer>(_ value: Value, to answer: (Value) -> Answer) -> Answer { answer(value) }
struct Example: View {
struct SomeGroup: Identifiable, RandomAccessCollection {
typealias Indices = CountableRange<Int>
public typealias Index = Int;
var id: Int
var displayName: String
var numbers: [Int]
public var endIndex: Index {
return numbers.count - 1
}
public var startIndex: Index {
return 0
}
public subscript(position: Int) -> Int {
get { return numbers[position] }
set { numbers[position] = newValue }
}
}
var someGroups: [SomeGroup] = {
return [
SomeGroup(id: 0, displayName: "First", numbers: [1, 2, 3, 4]),
SomeGroup(id: 1, displayName: "Second", numbers: [1, 3, 5, 7])
]
}()
var body: some View {
List {
ForEach(someGroups.indices) { section in
bind(self.someGroups[section]) { someGroup in
Section(header: Text(someGroup.displayName)) {
ForEach(someGroup.numbers) { number in
Text("\(number)")
}
.onDelete { self.delete(at: $0, in: section) }
}
}
}
}
.listStyle(.grouped)
}
func delete(at offsets: IndexSet, in section: Int) {
print("\(section), \(offsets.first!)")
}
}
非常感谢@rob-mayoff谁通过 Twitter 为我指明了这个解决方案的正确方向!
关于swift - IndexSet 引用部分的索引而不是行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56604167/