【发布时间】:2020-11-23 16:39:36
【问题描述】:
我有一个复杂的数据结构,它使用值类型(结构和枚举),我面临着让基本 CRUD 工作的主要问题。具体来说:
- 如何最好地“重新绑定” ForEach 中的值以供子视图编辑
- 如何移除/删除值
重新绑定
如果我有一组项目为@State 或@Binding,为什么没有一种简单的方法可以将每个元素绑定到视图?例如:
import SwiftUI
struct Item: Identifiable {
var id = UUID()
var name: String
}
struct ContentView: View {
@State var items: [Item]
var body: some View {
VStack {
ForEach(items, id: \.id) { item in
TextField("name", text: $item) // ???? Cannot find '$item' in scope
}
}
}
}
解决方法
我已经能够通过引入一个辅助函数来解决这个问题,以便在循环中为项目找到正确的索引:
struct ContentView: View {
@State var items: [Item]
func index(of item: Item) -> Int {
items.firstIndex { $0.id == item.id } ?? -1
}
var body: some View {
VStack {
ForEach(items, id: \.id) { item in
TextField("name", text: $items[index(of: item)].name)
}
}
}
}
但是,这感觉很笨重,而且可能很危险。
删除
一个更大的问题:你应该如何正确删除一个元素?这听起来像是一个基本问题,但请考虑以下问题:
struct ContentView: View {
@State var items: [Item]
func index(of item: Item) -> Int {
items.firstIndex { $0.id == item.id } ?? -1
}
var body: some View {
VStack {
ForEach(items, id: \.id) { item in
TextField("name", text: $items[index(of: item)].name)
Button( action: {
items.remove(at: index(of: item))
}) {
Text("Delete")
}
}
}
}
}
单击前几个项目上的“删除”按钮按预期工作,但尝试删除最后一个项目会导致 Fatal error: Index out of range...
我的特定用例没有映射到列表,所以我不能在那里使用删除助手。
引用类型
我知道引用类型使这一切变得更容易,特别是如果它们可以符合@ObservableObject。但是,我有一个庞大的、嵌套的、预先存在的值类型,它不容易转换为类。
任何帮助将不胜感激!
更新:建议的解决方案
- Deleting List Elements from SwiftUI's list:接受的答案提出了一个复杂的自定义绑定包装器。 Swift 功能强大,因此可以通过精心设计的变通方法解决许多问题,但我认为不需要精心设计的变通方法来获得可编辑项目列表。
- 使用状态或私有变量将视图标记为“已删除”,然后有条件地隐藏它们,以避免越界错误。这可以工作,但感觉就像是 hack,应该由框架处理。
【问题讨论】:
-
这能回答你的问题吗? Deleting list elements from SwiftUI's List
-
@NewDev 该问题的公认答案是一种有趣的方法,但它相当复杂且“自定义”。我一直希望有一种更简洁或更第一方的方式来做到这一点。
-
好吧,首先,您的“解决方法”中不需要额外的功能。链接的问题/答案为您提供了“第一方”方法:迭代索引并使用
$items[index]- 这是一个绑定。但问题仍然存在与删除 - 这似乎是一个错误,因为它已在 SwiftUI2 中解决 -
我现在正在使用 SwiftUI 2,但仍然面临这个错误(删除时超出范围)。其他场景是否修复?
-
嗯..确实,您的示例在 XCode12/iOS14 中仍然崩溃。但我对另一个问题的回答仍然有效——而且它是一个相当简单的包装器。