'How to change toggle on just one Core Data item using ForEach in SwiftUI?
How to change just one toggle in a list without subviews? I know how to make it work if I extract Subview from everything inside ForEach, but how to do it on one view?
I cannot use subview, because I have a problem later if I want to delete an item from this subview. It gives me some errors I don't know how to fix, so I am trying to make it on one view where I don't have this error.
The code for the list is quite simple:
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
var fetchRequest: FetchRequest<Item>
var items: FetchedResults<Item> { fetchRequest.wrappedValue }
@State private var doneStatus : Bool = false
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) {item in
HStack {
Text("\(item.name ?? "default item name")")
Spacer()
Toggle(isOn: self.$doneStatus) {
Text("Done")
}
.labelsHidden()
.onAppear {
self.doneStatus = item.done
}
.onTapGesture {
self.doneStatus.toggle()
item.done.toggle()
try? self.moc.save()
}
}
}
.onDelete(perform: removeItem)
}
.navigationBarTitle("Items")
.navigationBarItems(
leading:
Button(action: {
for number in 1...3 {
let item = Item(context: self.moc)
item.date = Date()
item.name = "Item \(number)"
item.done = false
do {
try self.moc.save()
}catch{
print(error)
}
}
}) {
Text("Add 3 items")
}
)
}
}
init() {
fetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Item.name, ascending: true)
])
}
func removeItem(at offsets: IndexSet) {
for offset in offsets {
let item = items[offset]
moc.delete(item)
}
try? moc.save()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//Test data
let testItem = Item.init(context: context)
testItem.date = Date()
testItem.name = "Item name"
testItem.done = false
return ContentView().environment(\.managedObjectContext, context)
}
}
I am using 1 Core Data Entity: Item. With 3 attributes: date (Date), done (Boolean), name (String).
PROBLEM
When I tap on one toggle, all other toggles change as well.
I couldn't find a solution working with Core Data. I guess maybe I should use .id instead of .self? And add another attribute to my entity: id (UUID). But I tried to do it and failed.
I will appreciate any kind of help.
Solution 1:[1]
You bound all Toggle
to one state... so
- remove this
// @State private var doneStatus : Bool = false
- bind
Toggle
dynamically to currently iteratingitem
(note: .onAppear/.onTapGesture not needed anymore)
Toggle(isOn: Binding<Bool>(
get: { item.done },
set: {
item.done = $0
try? self.moc.save()
})) {
Text()
}
.labelsHidden()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |