can anyone please help me to solve this? Please paste the same code in your Xcode and run it. Please follow the below steps:
struct Task : Identifiable {
var id = String()
var toDoItem = String()
var amount : Float = 0 //<-- Here
}
class TaskStore : ObservableObject {
@Published var tasks = [Task]()
}
struct Calculation: View {
@State var load1 = Float()
@State var load2 = Float()
@State var gp : Float = 0
@State var rate: Float = 0
@ObservedObject var taskStore = TaskStore()
@State private var birthDate = Date()
@State private var time1 = Date()
@State private var time2 = Date()
func addNewToDo() {
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
let dayoftime = formatter.string(from: birthDate)
let formatter2 = DateFormatter()
formatter2.dateFormat = "HH:mm"
let timeof1 = formatter2.string(from: time1)
let timeof2 = formatter2.string(from: time2)
taskStore.tasks.append(Task(id: String(taskStore.tasks.count + 1), toDoItem: "\(dayoftime) \(timeof1)-\(timeof2) total = \(rate.description) ", amount: rate))
//gp += rate
}
var body: some View {
NavigationView {
VStack {
HStack {
VStack(spacing: 1) {
Section(header: Text("Date") .foregroundColor(.black)
){
DatePicker(selection: $birthDate, displayedComponents: [.date]){Text(" Time")
}
}
VStack(spacing: 1) {
Section(header: Text("Time1") .foregroundColor(.black)){
DatePicker(selection: $time1, displayedComponents: [.hourAndMinute]){Text(" Time 1")
}
}
VStack(spacing: 1) {
Section(header: Text("Time2") .foregroundColor(.black)
){ DatePicker(selection: $time2, displayedComponents: [.hourAndMinute]){Text(" Time 1")
}
}
}
List {
Section(header:Text("load 2"))
{
TextField("Enter value of load 1", value: $load1, format: .number)
TextField("Enter value of load 1", value: $load2, format: .number)
}
HStack {
Button(String(format: "Add Load"), action: {
print(Rocky(mypay: rate))
gp += rate
})
Button(action: {
addNewToDo()
Rocky(mypay: rate)
},
label: {
Text(" ")
})
}
ForEach(self.taskStore.tasks) { task in
Text(task.toDoItem)
}
.onMove(perform : self.move)
.onDelete(perform : self.delete) //For each
}
.navigationBarTitle("SHIFTS")
.navigationBarItems(trailing: EditButton()) //List
Text("Total = $\(gp) ")
}.onAppear()
}
}
}
}
}
func Rocky(mypay: Float)
{
rate = load1 + load2
print("Sus \(gp)")
}
func move(from source : IndexSet, to destination : Int)
{
taskStore.tasks.move(fromOffsets: source, toOffset: destination)
}
func delete(at offsets : IndexSet) {
if let index = offsets.first { //<-- Here
let task = taskStore.tasks[index]
gp -= task.amount
}
taskStore.tasks.remove(atOffsets: offsets)
}
}
The issue is that ForEach
needs your items to be uniquely identifiable.
You're using an autogenerated id
for your tasks that is basically calculated as a current count of tasks in the storage:
taskStore.tasks.append(Task(id: String(taskStore.tasks.count + 1), ......`
So here's what's happening:
"1"
, "2"
and "3"
."1"
and "3"
. The only task that 's remaining in the array is the one with the ID of "2"
."2"
. So now you have two different tasks in the array with the same IDs.ForEach
method distinguishes your tasks by ID. It sees that it should render two rows of the list and both of them have the ID of "2"
. So it finds the first item with the ID equal to "2"
and renders it twice because it assumes that all items in the array have unique IDs (which the should have really).To solve this, use a UUID
as an identifier for a task.
You can also create an initializer that doesn't take an ID as an argument because the task will create a unique identifier for itself:
struct Task: Identifiable {
var id: UUID
var toDoItem: String
var amount: Float
init(toDoItem: String, amount: Float) {
self.id = UUID()
self.toDoItem = toDoItem
self.amount = amount
}
}
When creating a new task, use this new initializer without assigning items an explicit identifier:
taskStore.tasks.append(
Task(
toDoItem: "\(dayoftime) \(timeof1)-\(timeof2) total = \(rate.description) ",
amount: rate
)
)
That's it, now all tasks will have unique identifiers and will render correctly.