swift - IndexSet 引用部分的索引而不是行

标签 swift list foreach swiftui

我想通过使用 .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 中的错误吗?

如果不是,那我如何获取该行的索引?

最佳答案

简单来说,这个问题的解决方案是通过以下方式将部分传递给您的删除方法:

  1. 对您的源数据采用RandomAccessCollection
  2. 在外部 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/

相关文章:

使用用户输入创建链表会在 CodeBlocks 上返回错误 0xC0000005

python - 在 python 中查找(并保留)子列表的副本

php - 在 ArrayObject 中循环和取消设置时 foreach 出现意外行为。一个项目被忽略

c# - 如何遍历由 char 分隔的整个数组,并且该 char 不在最后一个元素之后?

ios - 为什么 UIImageView 只旋转一次?

ios - 编辑模式下具有自定义单元格的 UITableView 不会将单元格推倒

ios - 如何观察系统警报消失

iOS Swift 在 NSSUrlSession Completion Handler 之后在 Seque 中传递数据

Python:如何过滤字典列表以获取一个键的所有值

php - 是否有更好的循环遍历这些 PHP 代码的方法,也许使用 foreach 循环?