一尘不染

将ForEach循环与Binding一起使用会导致数组缩小时索引超出范围(SwiftUI)

swift

我有一个应用

  1. 分别提取数组的每个元素(通过索引)
  2. 然后将其绑定到可以利用单个元素的结构(查看和编辑)

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

据我所知,这是因为:在用更改后的数组刷新循环之后,在某种程度上创建的视图还没有完全删除,仍然尝试访问超出范围的部分。但这就是我自己能想到的

这是我的示例代码:

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

有没有更好的方法可以满足上述两个要求而又不引起此问题,或者有什么方法可以避免此问题?任何答复都表示赞赏。


阅读 299

收藏
2020-07-07

共1个答案

一尘不染

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

问题是建筑。它是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 == $0})!])
            .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 == $0})!
                self.data.textArray[index] = "Z"
            }){
                Text("Change String ")
                .padding()
            }
        }
    }    
}

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