首页 > 解决方案 > 带有 Toggle 和 NavigationLink 的 SwiftUI onDelete 列表

问题描述

我指的是我已经问过的两个问题,Asperi 已经很好地回答了:SwiftUI ForEach with .indices() does not update after onDeleteSwiftUI onDelete List with Toggle

现在我尝试ForEach用 a修改闭包NavigationLink,突然应用程序再次崩溃

Thread 1: Fatal error: Index out of range

当我尝试滑动删除时。

代码:

class Model: ObservableObject {
    @Published var name: String
    @Published var items: [Item]
    
    init(name: String, items: [Item]) {
        self.name = name
        self.items = items
    }   
}

struct Item: Identifiable {
    var id = UUID()
    var isOn: Bool
}

struct ContentView: View {
    @EnvironmentObject var model: Model
    
    var body: some View {
        NavigationView {
            List {
                ForEach(model.items) {item in
                    NavigationLink(destination: DetailView(item: self.makeBinding(id: item.id))) {
                        Toggle(isOn: self.makeBinding(id: item.id).isOn)
                        {Text("Toggle-Text")}
                    }
                }.onDelete(perform: delete)
            }
        }
    }
    
    func delete(at offsets: IndexSet) {
        self.model.items.remove(atOffsets: offsets)
    }
    
    func makeBinding(id: UUID) -> Binding<Item> {
        guard let index = self.model.items.firstIndex(where: {$0.id == id}) else {
            fatalError("This person does not exist")
        }
        return Binding(get: {self.model.items[index]}, set: {self.model.items[index] = $0})
    }
}

struct DetailView: View {
    @Binding var item: Item
    var body: some View {
        Toggle(isOn: $item.isOn) {
            Text("Toggle-Text")
        }
    }
}

它没有NavigationLinkOR 没有Toggle. 所以在我看来,我只能makeBinding在这个闭包中使用 -Function 一次。

感谢帮助

标签: listbindingswiftuitoggleswiftui-navigationlink

解决方案


无论有没有导航链接,您的代码都会对我崩溃。有时只有当我删除数组中的最后一个对象时。看起来它仍在尝试从数组中访问索引。您在上面链接的示例的不同之处在于它们不用于EnvironmentObject访问数组。将数组直接存储在@State.

我想出了一些不同的方法,将 Item 声明为ObservedObject,然后简单地将其传递给子视图,您可以在其中将它们的值用作绑定,而无需任何功能。

我将项目更改为..

class Item: ObservableObject {
    var id = UUID()
    var isOn: Bool
    
    init(id: UUID, isOn: Bool)
    {
        self.id = id
        self.isOn = isOn
    }
}

将 ContentView 更改为此..

struct ContentView: View {
    @EnvironmentObject var model: Model
    
    var body: some View {
        NavigationView {
            List {
                ForEach(model.items, id:\.id) {item in
                    NavigationLink(destination: DetailView(item: item)) {
                        Toggler(item: item)
                    }
                }.onDelete(perform: delete)
            }
        }
    }

我将 Toggle 外包给了另一个视图,我们将 ObservedObject 传递给,DetailView 也是如此。

struct Toggler: View {
    @ObservedObject var item : Item
    
    var body : some View
    {
        Toggle(isOn: $item.isOn)
        {Text("Toggle-Text")}
    }
}

struct DetailView: View {
    @ObservedObject var item: Item
    var body: some View {
        Toggle(isOn: $item.isOn) {
            Text("Toggle-Text")
        }
    }
}

它们都采用Itemas ObservedObject 并将其用作Toggle.


推荐阅读