当数组收缩时,将 ForEach 循环与 Binding 结合使用会导致索引超出范围 (SwiftUI)
Using ForEach loop with Binding causes index out of range when array shrinks (SwiftUI)
我有一个应用
- Individually extracts every element of an array (through indices)
- 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折:
- 您正在复制您独特的真实来源。 ForEach 循环 Textfield 但您正在通过 Binding 传递副本。始终致力于单一事实来源
- 结合 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
我有一个应用
- Individually extracts every element of an array (through indices)
- 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折:
- 您正在复制您独特的真实来源。 ForEach 循环 Textfield 但您正在通过 Binding 传递副本。始终致力于单一事实来源
- 结合 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