1

到目前为止,要在动态集合中的元素和 List 的行之间设置绑定,我们必须这样做:

List(Array(zip(data.indices, data)), id: \.1.id) { index, _ in
    HStack {
        Text((index + 1).description)
        TextField("", text: Binding(
            get: { data[index].text },
            set: { data[index].text = $0 }
        ))
    }
}

我们需要: 用于绑定的元素的索引;+ 的元素标识符List(以避免奇怪的动画);以及Binding在删除最后一行时避免崩溃的自定义。

这很复杂(我不确定它是否非常有效)。自 WWDC21 以来,我们有了一个新语法(可以反向部署):

List($data) { $item in
    HStack {
        Text("Index ?")
        TextField("", text: $item.text)
    }
}

它更干净。

但是,虽然强烈建议使用这种新语法,但如果能够在闭包中访问元素的索引,那就太好了。你知道我们该怎么做吗?

编辑 :

我试过这个(它有效),但我觉得这不是正确的方法:

let d = Binding(get: {
    Array(data.enumerated())
}, set: {
    data = $0.map {$0.1}
})
List(d, id: \.1.id) { $item in
    HStack {
        Text("\(item.0 + 1)")
        TextField("", text: $item.1.text)
    }
}
4

2 回答 2

3

您可以自己构建包装器:

struct ListIndexed<Content: View>: View {
    let list: List<Never, Content>
    
    init<Data: MutableCollection&RandomAccessCollection, RowContent: View>(
        _ data: Binding<Data>,
        @ViewBuilder rowContent: @escaping (Data.Index, Binding<Data.Element>) -> RowContent
    ) where Content == ForEach<[(Data.Index, Data.Element)], Data.Element.ID, RowContent>,
    Data.Element : Identifiable,
    Data.Index : Hashable
    {
        list = List {
            ForEach(
                Array(zip(data.wrappedValue.indices, data.wrappedValue)),
                id: \.1.id
            ) { i, _ in
                rowContent(i, Binding(get: { data.wrappedValue[i] }, set: { data.wrappedValue[i] = $0 }))
            }
        }
    }
    
    var body: some View {
        list
    }
}

用法:

ListIndexed($items) { i, $item in
    HStack {
        Text("Index \(i)")
        TextField("", text: $item.text)
    }
}
于 2021-08-19T19:48:37.797 回答
0

如果您的项目是 Equatable,您可以使用它firstIndex(of:)来获取它们的索引:

List($data) { $item in
    HStack {
        Text("Index \(data.firstIndex(of: item)! + 1)")
        TextField("", text: $item.text)
    }
}
于 2021-08-19T17:22:13.293 回答