当数组收缩时,将 ForEach 循环与 Binding 结合使用会导致索引超出范围 (SwiftUI) [英] Using ForEach loop with Binding causes index out of range when array shrinks (SwiftUI)

查看:20
本文介绍了当数组收缩时,将 ForEach 循环与 Binding 结合使用会导致索引超出范围 (SwiftUI)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序

<块引用>

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

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

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

这是我的示例代码:

导入 SwiftUI结构测试:查看{@State var TextArray = ["A","B","C"]变量体:一些视图{虚拟堆栈{ForEach(TextArray.indices, id: .self){index in//文本视图文本视图(文本:self.$TextArray[index]).填充()}//数组修改按钮按钮(动作:{self.TextArray = ["A","B"]}){Text("收缩数组").填充()}}}}结构文本视图:视图{@Binding var text:String变量体:一些视图{文字(文字)}}#if 调试struct test_Previews: PreviewProvider {静态 var 预览:一些视图 {测试()}}#万一

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

解决方案

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

问题在于架构.它是 2 折:

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

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

<代码>导入 SwiftUI类数据源:ObservableObject {@Published var textArray = ["A","B","C"]}结构测试:查看{@EnvironmentObject 变量数据:数据源变量体:一些视图{虚拟堆栈{ForEach(self.data.textArray, id: .self) {text in文本视图(文本:self.data.textArray[self.data.textArray.firstIndex(其中:{text == $0})!]).填充()}//数组修改按钮按钮(动作:{self.data.textArray.removeLast()}){Text("收缩数组").填充()}}}}结构文本视图:视图{@EnvironmentObject 变量数据:数据源无功文本:字符串变量体:一些视图{虚拟堆栈{文字(文字)按钮(动作:{让 index = self.data.textArray.firstIndex(其中:{self.text == $0})!self.data.textArray[index] = "Z"}){文本(更改字符串").填充()}}}}#if 调试struct test_Previews: PreviewProvider {静态 var 预览:一些视图 {测试().环境对象(数据源())}}#万一

I have an app that

  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)

But every time the array reduces in size, it causes an index out of range error that is not directly because of my code

As far as I know, it's because: after the loop refreshes with the changed array, the views it created before somehow isn't completely removed and still trying access the out of range part. But that's all I can figure out myself

Here is my sample code:

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

Is there any better way to satisfy the two requirements above without causing this problem or any way to circumvent this problem ? Any responses are really appreciated.

解决方案

Finally got the ins and outs of that issue that I was experiencing myself.

The problem is architectural. It is 2 folds:

  1. You are making a copy of your unique source of truth. ForEach loops Textfield but you are passing a copy through Binding. Always work on the single source of truth
  2. Combined with ForEach ... indices is supposed to be a constant range (hence the out of range when you remove an element)

The below code works because it loops through the single source of truth without making a copy and always updates the single source of truth. I even added a method to change the string within the subview since you originally passed it as a binding, I imagine you wanted to change it at some point


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

这篇关于当数组收缩时,将 ForEach 循环与 Binding 结合使用会导致索引超出范围 (SwiftUI)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆