当数组收缩时,将 ForEach 循环与 Binding 结合使用会导致索引超出范围 (SwiftUI)

Using ForEach loop with Binding causes index out of range when array shrinks (SwiftUI)

我有一个应用

  1. Individually extracts every element of an array (through indices)
  2. Then bind it to a struct that can make use of that single element (viewing and editing)

但是每次数组大小减小时,它都会导致索引超出范围错误,这不是直接因为我的代码

据我所知,这是因为:在使用更改后的数组刷新循环后,它之前创建的视图以某种方式未完全删除,并且仍在尝试访问超出范围的部分。但这就是我自己能弄清楚的

这是我的示例代码:

import SwiftUI

struct test: View {
    @State var TextArray = ["A","B","C"]
    var body:some View {
        VStack{
        ForEach(TextArray.indices, id: \.self){index in
            //Text View
            TextView(text: self.$TextArray[index])
            .padding()
            }
            //Array modifying button
            Button(action: {
                self.TextArray = ["A","B"]
            }){
                Text(" Shrink array ")
                .padding()
            }
        }
    }
}

struct TextView:View {
    @Binding var text:String
    var body:some View {
    Text(text)
    }
}




#if DEBUG
struct test_Previews: PreviewProvider {
    static var previews: some View {
        test()
    }
}
#endif

有没有什么更好的办法既能满足以上两个要求又不会引起这个问题,或者有什么办法可以规避这个问题?非常感谢任何回复。

@State 似乎无法处理这个问题,但 ObservableObject 可以。

除了我最好的猜测,我不知道为什么,那就是 @State 试图通过预测用户想要的东西来避免重绘,但这样做不支持这一点。

与此同时,ObservableObject 会根据每个小改动重绘所有内容。有效。

class FlashcardData: ObservableObject {
    @Published var textArray = ["A","B","C"]

    func updateData() {
        textArray = ["A","B"]
    }
}

struct IndexOutOfRangeView: View {
    @ObservedObject var viewModel = FlashcardData()

    var body:some View {
        VStack{
            ForEach(viewModel.textArray.indices, id: \.self){ index in
                TextView(text: self.$viewModel.textArray[index])
                    .padding()
            }
            Button(action: {
                self.viewModel.textArray = ["A","B"]
            }){
                Text(" Shrink array ")
                    .padding()
            }
        }
    }
}

struct TextView:View {
    @Binding var text:String
    var body:some View {
        Text(text)
    }
}

终于了解了我自己遇到的那个问题的来龙去脉。

问题是结构性的。是2折:

  1. 您正在复制您独特的真实来源。 ForEach 循环 Textfield 但您正在通过 Binding 传递副本。始终致力于单一事实来源
  2. 结合 ForEach ...索引应该是一个恒定范围(因此当您删除元素时超出范围)

下面的代码之所以有效,是因为它在不制作副本的情况下循环遍历单一事实来源,并且始终更新单一事实来源。我什至添加了一个方法来更改子视图中的字符串,因为你最初将它作为绑定传递,我想你想在某个时候更改它


import SwiftUI

class DataSource: ObservableObject {
    @Published var textArray = ["A","B","C"]
}

struct Test: View {

    @EnvironmentObject var data : DataSource

    var body:some View {
        VStack{
            ForEach(self.data.textArray , id: \.self) {text in
                TextView(text: self.data.textArray[self.data.textArray.firstIndex(where: {text == [=10=]})!])
            .padding()
            }

            //Array modifying button
            Button(action: {
                self.data.textArray.removeLast()
            }){
                Text(" Shrink array ")
                .padding()
            }
        }
    }
}

struct TextView:View {

    @EnvironmentObject var data : DataSource

    var text:String

    var body:some View {
        VStack {
            Text(text)
            Button(action: {
                let index = self.data.textArray.firstIndex(where: {self.text == [=10=]})!
                self.data.textArray[index] = "Z"
            }){
                Text("Change String ")
                .padding()
            }
        }
    }    
}

#if DEBUG
struct test_Previews: PreviewProvider {
    static var previews: some View {
        Test().environmentObject(DataSource())
    }
}
#endif