Несколько ForEach в одном списке против нескольких списков с одним ForEach

Я новичок в iOS и SwiftUI и пытаюсь создать приложение для "целей". Код, на который я ссылаюсь, представляет собой простой список с неполными целями, перечисленными над целями, отмеченными как завершенные. Я столкнулся с этой интересной проблемой при попытке поставить несколькоForEachвнутри того же List.

Когда я использовал приведенный ниже код, нажатие на неполную цель (из первого foreach) переместило бы эту цель ниже границы, но GoalView() конструктор не запускался повторно, поэтому цель все еще казалась неполной, и, более того, он все еще сохранял исходное закрытие (goal.markComplete()), что привело бы к сбою. Стоит отметить, чтоtoday является @ObservedObject, и он обновляется вызовами markComplete() а также markIncomplete()

List {
    ForEach(today.incompleteGoals, id: \.id) { goal in
        GoalView(goal: goal, isCompleted: false)
            .onTapGesture { goal.markComplete() }
    }}
    Text("---Boundary---")
    ForEach(today.completedGoals, id: \.id) { goal in
        GoalView(goal: goal, isCompleted: true)
            .onTapGesture { goal.markIncomplete() }
    }}
}

Однако, когда я переключаюсь на использование двух составных списков (см. Ниже), проблема исчезает. Нажатие на достижение цели перемещает ее в нижний список, а значокGoalView() конструктор запускается, изменяя внешний вид и поведение цели.

VStack {
    List {
        ForEach(today.incompleteGoals, id: \.id) { goal in
            GoalView(goal: goal, isCompleted: false)
                .onTapGesture { goal.markComplete() }
    }}}
    Text("---Boundary---")
    List {
        ForEach(today.completedGoals, id: \.id) { goal in
            GoalView(goal: goal, isCompleted: true)
                .onTapGesture { goal.markIncomplete() }
    }}}
}

В чем заключается принципиальная разница, которая заставит эту нижнюю реализацию повторно запустить GoalView() в ForEachзакрытие, тогда как верхняя имп. нет? Меня особенно смущает, почему обе реализации перемещают завершенную цель ниже Границы, несмотря на то, что верхняя, кажется, никогда не выполняет закрытие, прикрепленное ко второмуForEach. Любые объяснения / советы будут очень признательны!

РЕДАКТИРОВАТЬ: today (тип Day) а также goal (тип Goal) оба NSManagedObjects / CoreData сущности. Ниже приведено мое определение дляcompletedGoals от расширения до Day, что должно пролить свет на то, как эти два понятия определены и связаны. КаждыйDay называется отношениями completedGoals_ а также incompleteGoals_ которые представляют собой неупорядоченные наборы Целей, связанных с этим Днем.

var completedGoals: Array<Goal> {
    get {
        let result = (completedGoals_ as? Set<Goal>) ?? []
        return Array(result).sorted(by: ...)
    }
    set { completedGoals_ = Set(newValue) as NSSet } 
}

а вот примерная реализация markComplete() от расширения до Goal (daysThatDidntComplete_ является обратной зависимостью от completedGoals_, и то же самое с неполным):

func markComplete(on day: Day, context: NSManagedObjectContext) {
    // ...check & crash if self (goal) wasn't already incomplete on day
    removeFromDaysThatDidntComplete_(day)  // provided by CoreData
    addToDaysThatCompleted_(day)  // provided by CoreData
    try? context.save()
}

1 ответ

Код для меня не поддается тестированию, поэтому просто идея - попробуйте следующее

List {
    ForEach(today.incompleteGoals, id: \.id) { goal in
        GoalView(goal: goal, isCompleted: false)
            .onTapGesture { 
                goal.markComplete() 
                self.today.objectWillChange.send()     // << here !!
            }
    }}
    Text("---Boundary---")
    ForEach(today.completedGoals, id: \.id) { goal in
        GoalView(goal: goal, isCompleted: true)
            .onTapGesture { 
                goal.markIncomplete() 
                self.today.objectWillChange.send()     // << here !!
            }
    }}
}
Другие вопросы по тегам