同一列表中的多个列表与一个列表中的多个列表

时间:2020-07-30 21:24:46

标签: ios swift swiftui swiftui-list swiftui-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)都是NSManagedObject / 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 个答案:

答案 0 :(得分: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 !!
            }
    }}
}