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

Ngu*_*Hào 10 swift swiftui

我有一个应用程序

  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
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来满足上述两个要求而不会导致此问题或有任何方法可以规避此问题?任何回应都非常感谢。

Fab*_*ian 6

@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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 但是关于 @FetchRequest 和过滤结果的问题。它使索引超出范围 (4认同)

Gra*_*eph 5

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

问题是架构。它是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
Run Code Online (Sandbox Code Playgroud)

  • 哈哈,我收回这句话,我很困惑,因为它本身是“viewModel.TextArray”而不是“textArray”。我想知道为什么它会起作用,你知道吗? (2认同)