ios SwiftUI抛出“致命错误:为没有显式索引的应用程序添加元素时“索引超出范围

bpzcxfmw  于 2023-02-01  发布在  iOS
关注(0)|答案(3)|浏览(120)

为什么,在下面的应用程序中,当点击到'尼斯餐厅'并试图添加一个贡献者,应用程序崩溃的错误:一个月一个月
在Xcode调试器中,这个错误没有明显有用的堆栈跟踪,并且直接指向'@main '行。
代码中没有使用显式数组索引,也没有使用.first之类的成员。
我正在使用Xcode版本13.4.1(13F100)我正在使用模拟器:iPhone 13操作系统15.5(19F70)

import SwiftUI

struct CheckContribution: Identifiable {
    let id: UUID = UUID()
    var name: String = ""
}

struct Check: Identifiable {
    var id: UUID = UUID()
    var title: String
    var contributions: [CheckContribution]
}

let exampleCheck = {
    return Check(
        title: "Nice Restaurant",
        contributions: [
            CheckContribution(name: "Bob"),
            CheckContribution(name: "Alice"),
        ]
    )
}()

struct CheckView: View {
    @Binding var check: Check
    @State private var selectedContributor: CheckContribution.ID? = nil
    
    func addContributor() {
        let newContribution = CheckContribution()
        check.contributions.append(newContribution)
        selectedContributor = newContribution.id
    }
    
    var body: some View {
        List {
            ForEach($check.contributions) { $contribution in
                TextField("Name", text: $contribution.name)
            }
            Button(action: addContributor) {
                Text("Add Contributor")
            }
        }
    }
}

@main
struct CheckSplitterApp: App {
    @State private var checks: [Check] = [exampleCheck]
    var body: some Scene {
        WindowGroup {
            NavigationView {
                List {
                    ForEach($checks) { $check in
                        NavigationLink(destination: {
                            CheckView(check: $check)
                        }) {
                            Text(check.title).font(.headline)
                        }
                    }
                }
            }
        }
    }
}

我注意到:

  • 如果我展开ForEach($checks),则不会发生崩溃(但我需要保留ForEach,以便列出所有检查)
  • 如果我没有绑定到CheckContribution(ForEach($check.contributions) { $contribution in,则不会发生崩溃(但我需要绑定,以便子视图可以修改CheckContribution
  • 如果我不设置selectedContributor,那么就不会发生崩溃(但我需要在真正的应用程序中设置selectedContributor以用于导航)
2lpgd968

2lpgd9681#

我能找到的最简洁的方法是将嵌套的ForEach进一步分离到一个子视图中,并将contributors数组绑定到它。

struct CheckView: View {
    @Binding var check: Check
    @State private var selectedContributor: CheckContribution.ID? = nil

    func addContributor() {
        let newContribution = CheckContribution()
        check.contributions.append(newContribution)
        selectedContributor = newContribution.id
    }

    var body: some View {
        List {
            ContributionsView(contributions: $check.contributions)

            Button(action: addContributor) {
                Text("Add Contributor")
            }

            // Test that changing other properties still works.
            Button("Change title", action: changeTitle)
        }
        .navigationTitle(check.title)
    }

    func changeTitle() {
        check.title = "\(Int.random(in: 1...100))"
    }
}

struct ContributionsView: View {
    @Binding var contributions: [CheckContribution]

    var body: some View {
        ForEach($contributions) { $contribution in
            TextField("Name", text: $contribution.name)
        }
    }
}

我仍然不确定SwiftUI的内部结构,以及为什么它会这样工作。我希望它能有所帮助。也许另一个更有经验的用户可以对此提供一个清晰的解释。

4smxwvx5

4smxwvx52#

如果您确实希望Button位于List中,那么您可以尝试使用单独视图的方法,对我来说效果很好:

struct CheckView: View {
    @Binding var check: Check
    @State private var selectedContributor: CheckContribution.ID? = nil
    
    var body: some View {
        List {
            ForEach($check.contributions) { $contribution in
                TextField("Name", text: $contribution.name)
            }
            AddButtonView(check: $check)  // <-- here
        }
    }
}

struct AddButtonView: View {
    @Binding var check: Check
    
    func addContributor() {
        let newContribution = CheckContribution(name: "new contribution")
        check.contributions.append(newContribution)
    }
    
    var body: some View {
        Button(action: addContributor) {
            Text("Add Contributor")
        }
    }
}
q8l4jmvw

q8l4jmvw3#

我也有同样的错误,但用的是Tabview,而且摔倒只在iOS 15上,但在iOS 16上运行完美,没有福尔斯。
我尝试了通过索引和通过检查查找范围内的索引,但没有任何帮助。
在调试过程中找到了解决方案:我注意到它甚至在predstavlenie出现之前就在下降了(它工作的是Appear)。我做了一个简单的检查,看看数据数组是否为空

if !dataArray.isEmpty {
    TabView(selection: $selection) {
        ForEach(dataArray, id: \.self) { item in
            ...
        } 
    }
}

而且它起作用了--iOS 15上再也没有崩溃。显然iOS 16之前的空数组处理出现了一些问题。

相关问题