0

我有以下代码,它只能在 Xcode 13 beta 中编译,而不是在 Xcode 12 中编译。该项目针对 iOS 13 及更高版本。请注意,它使用可以将绑定传递给数组元素的新语法,这是我在项目中需要的(但此演示代码可以不用)。

class MyViewModel: ObservableObject {
    @Published var model: MyModel
    
    init() {
        self.model = MyModel(components: ["alpha", "bravo", "charlie", "delta", "echo", "foxtrot"])
    }
}

struct MyModel {
    var components: [String]
}

struct ContentView: View {
    @EnvironmentObject var viewModel: MyViewModel

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Components")) {
                    ForEach(self.$viewModel.model.components, id: \.self) { $component in
                        Text(component)
                    }
                    .onDelete(perform: delete)
                }
            }
        }.navigationViewStyle(StackNavigationViewStyle())
    }

    private func delete(_ indices: IndexSet) {
        for index in indices {
            self.viewModel.model.components.remove(at: index)
        }
    }
}

此外, ContentView() 必须注入正确的环境对象,即:

struct FormDeleteCrashDemo_iOS13App: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(MyViewModel())
        }
    }
}

在运行 iOS 14 的 iPad 或 iPhone 上运行此代码时,用户可以向左滑动删除。删除最下面一行就可以了。删除任何其他行将崩溃并出现以下错误:

Swift/ContiguousArrayBuffer.swift:580: Fatal error: Index out of range

当我用 替换FormList,不会发生崩溃。为什么?

4

1 回答 1

1

这对我来说似乎是一个错误。崩溃恰好发生在Form(与一起正常工作List

我已经将它报告给反馈助手,所以我们希望它会在发布前得到修复。

同时你可以试试这个CustomForEach,灵感来自我的ListIndexed。所有动画看起来都很好。

struct CustomForEach<Data: MutableCollection&RandomAccessCollection, RowContent: View, ID: Hashable>: View, DynamicViewContent where Data.Index : Hashable
{
    let forEach: ForEach<[(Data.Index, Data.Element)], ID, RowContent>
    
    init(_ data: Binding<Data>,
         @ViewBuilder rowContent: @escaping (Binding<Data.Element>) -> RowContent
    ) where Data.Element: Identifiable, Data.Element.ID == ID {
        forEach = ForEach(
            Array(zip(data.wrappedValue.indices, data.wrappedValue)),
            id: \.1.id
        ) { i, _ in
            rowContent(Binding(get: { data.wrappedValue[i] }, set: { data.wrappedValue[i] = $0 }))
        }
    }
    
    init(_ data: Binding<Data>,
         id: KeyPath<Data.Element, ID>,
         @ViewBuilder rowContent: @escaping (Binding<Data.Element>) -> RowContent
    ) {
        forEach = ForEach(
            Array(zip(data.wrappedValue.indices, data.wrappedValue)),
            id: (\.1 as KeyPath<(Data.Index, Data.Element), Data.Element>).appending(path: id)
        ) { i, _ in
            rowContent(Binding(get: { data.wrappedValue[i] }, set: { data.wrappedValue[i] = $0 }))
        }
    }
    
    init(_ data: Binding<Data>,
         @ViewBuilder rowContent: @escaping (Binding<Data.Element>) -> RowContent
    ) where ID == Data.Element {
        forEach = ForEach(
            Array(zip(data.wrappedValue.indices, data.wrappedValue)),
            id: \.1
        ) { i, _ in
            rowContent(Binding(get: { data.wrappedValue[i] }, set: { data.wrappedValue[i] = $0 }))
        }
    }
    
    var body: some View {
        forEach
    }
    
    var data: [(Data.Index, Data.Element)] {
        forEach.data
    }
}
于 2021-08-25T08:33:01.560 回答