在 ForEach 中删除数组元素时,SwiftUI 没有索引

标签 swift swiftui

我在这里查看了不同的问题,但不幸的是我找不到答案。这是我的代码:

SceneDelegate.swift

...
let contentView = ContentView(elementHolder: ElementHolder(elements: ["abc", "cde", "efg"]))
...
window.rootViewController = UIHostingController(rootView: contentView)

ContentView.swift
class ElementHolder: ObservableObject {

    @Published var elements: [String]

    init(elements: [String]) {
        self.elements = elements
    }
}

struct ContentView: View {

    @ObservedObject var elementHolder: ElementHolder

    var body: some View {
        VStack {

            ForEach(self.elementHolder.elements.indices, id: \.self) { index in
                SecondView(elementHolder: self.elementHolder, index: index)
            }

        }
    }
}

struct SecondView: View {

    @ObservedObject var elementHolder: ElementHolder
    var index: Int

    var body: some View {
        HStack {
            TextField("...", text: self.$elementHolder.elements[self.index])
            Button(action: {
                self.elementHolder.elements.remove(at: self.index)
            }) {
                Text("delete")
            }
        }
    }

}

当按下删除按钮时,应用程序因索引越界错误而崩溃。

有两个奇怪的事情,应用程序在运行时

1) 删除 VStack 并将 ForEach 放入 bodyContentView.swift

2)你把SecondView的代码直接放到ForEach

只是一件事:我真的需要 ObservableObject ,这段代码只是另一个代码的简化。

更新

我更新了我的代码并将 Text 更改为 TextField ,因为我不能只传递一个字符串,我需要双向连接。

最佳答案

这是可能的解决方案 - 使 SecondView 的主体不依赖 ObservableObject

使用 Xcode 11.4/iOS 13.4 测试 - 没有崩溃

struct SecondView: View {

    @ObservedObject var elementHolder: ElementHolder
    var index: Int
    let value: String

    init(elementHolder: ElementHolder, index: Int) {
        self.elementHolder = elementHolder
        self.index = index
        self.value = elementHolder.elements[index]
    }

    var body: some View {
        HStack {
            Text(value)     // not refreshed on delete
            Button(action: {
                self.elementHolder.elements.remove(at: self.index)
            }) {
                Text("delete")
            }
        }
    }
}

另一个可能的解决方案是不要在 ElementHolder 中观察 SecondView ... 不需要显示和删除它 - 也没有崩溃
struct SecondView: View {

    var elementHolder: ElementHolder // just reference
    var index: Int

    var body: some View {
        HStack {
            Text(self.elementHolder.elements[self.index])
            Button(action: {
                self.elementHolder.elements.remove(at: self.index)
            }) {
                Text("delete")
            }
        }
    }
}

更新: 文本字段的 SecondView 变体(仅更改文本字段本身)
struct SecondViewA: View {

    var elementHolder: ElementHolder
    var index: Int

    var body: some View {
        HStack {
            TextField("", text: Binding(get: { self.elementHolder.elements[self.index] },
                set: { self.elementHolder.elements[self.index] = $0 } ))
            Button(action: {
                self.elementHolder.elements.remove(at: self.index)
            }) {
                Text("delete")
            }
        }
    }
}

关于在 ForEach 中删除数组元素时,SwiftUI 没有索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61430999/

相关文章:

javascript - OSX 上的 Swift : WebView stringByEvaluatingJavaScriptFromString doesn't work

ios - SceneKit 在移动相机时遇到测试错误

ios - SWIFTUI 在 TabView 中隐藏当前 View 的 navigationBarBackButton

ios - SwiftUI Firestore 查询游标分页不起作用

ios - 为什么我收到此错误 : Ambiguous reference to member 'fetch(with:parse:completion:)'

ios - UIGestureRecognizerStateEnded 的 Swift 等价物是什么?

swift - 将 'ClassName' 重新定义为生成 header 中的不同类型符号

SwiftUI:如何更改 TabView 高度?

SwiftUI - 取决于元素计数的可调整大小的列表高度

ios - 在点击期间格式化 TextField 文本