带有Toggle和NavigationLink的SwiftUI onDelete列表

时间:2020-07-18 09:43:49

标签: list binding swiftui toggle swiftui-navigationlink

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

现在,我尝试使用ForEach修改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")
        }
    }
}

它在没有NavigationLink或没有Toggle的情况下有效。因此在我看来,在此闭包中我只能使用一次makeBinding-Function。

感谢帮助

1 个答案:

答案 0 :(得分:0)

即使没有导航链接,您的代码也崩溃了。有时仅当我删除数组中的最后一个对象时。看起来它仍在尝试从数组中访问索引。与您上面链接的示例的区别在于,它们没有使用EnvironmentObject来访问数组。将数组直接存储在@State中。

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

我将商品更改为..

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")
        }
    }
}

它们都将Item用作ObservedObject,并将其用作Toggle的绑定。