ios - 同一列表中的多个 ForEach 与具有一个 ForEach 的多个列表
问题描述
我是 iOS 和 SwiftUI 的新手,正在制作一个“目标”应用程序。我引用的代码是一个简单的列表,在标记为完成的目标之上列出了不完整的目标。我在尝试将多个ForEach
s 放入同一个 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
) 都是NSManagedObject
s / CoreData 实体。下面包括我对completedGoals
from 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()
从扩展到的粗略实现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()
}
解决方案
代码对我来说是不可测试的,所以只是一个想法 - 尝试以下
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 !!
}
}}
}
推荐阅读
- alert - Grafana 警报当两个命中被记录时,它们之间有 5 分钟
- java - Neo4j Spring 数据映射到 Java API stackoverflow
- javascript - 在动作函数中使用 async/await 传递数据
- angular - Getter/Setter 是否应该包含除 Get/Set 之外的其他方法和代码?
- c# - 在 xamarin 表单 4.4.0 中将图像添加到轮播视图
- python - 在空格格式的报告中解析多行标题
- rest - 如何在不通知的情况下以编程方式(例如 Rest API)将用户添加到 AzureDevOps?
- clearcase - 如何签出然后一次复制到符号链接目的地的所有文件?
- r - 在闪亮的应用程序中更改 dateRangeInput() 日记框的位置
- r - R循环根据数据框中的值将文件复制到不同的文件夹中