0

我是 iOS 和 SwiftUI 的新手,正在制作一个“目标”应用程序。我引用的代码是一个简单的列表,在标记为完成的目标之上列出了不完整的目标。我在尝试将多个ForEachs 放入同一个 s时遇到了这个有趣的问题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(type Day) 和goal(type Goal) 都是NSManagedObjects / CoreData 实体。下面包括我对completedGoalsfrom an extension to的定义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()从扩展到的粗略实现GoaldaysThatDidntComplete_是与的反比关系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()
}
4

1 回答 1

0

代码对我来说是不可测试的,所以只是一个想法 - 尝试以下

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 !!
            }
    }}
}
于 2020-07-31T19:20:29.443 回答