【发布时间】:2021-05-07 02:36:03
【问题描述】:
我是一名 Windows C# 开发人员,是 iOS/SwiftUI 开发的新手,我想我已经在这方面陷入了困境。
我有一个带有@Binding 变量的视图:
struct DetailView: View {
@Binding var project: Project
项目是一个包含任务数组的对象。我正在遍历项目的任务以显示其名称和一个切换,其状态由任务的变量 isComplete 确定。
ForEach(filteredTasks.indices, id: \.self) { idx in
HStack {
Text(filteredTasks[idx].phase)
.font(.caption)
Spacer()
Text(filteredTasks[idx].name)
Spacer()
Toggle("", isOn: self.$filteredTasks[idx].isComplete)
}
}
}
我花了很长时间才找到这段代码,我发现我必须按照一个带有“索引”选项的示例来让切换分别在每个任务上工作,并确保它的 isComplete 值已保存。
接下来,我想根据具有 Planning、Construction 或 Final 值的任务变量阶段过滤任务列表。所以我创建了 4 个按钮(每个阶段一个,然后是一个“所有任务”以返回完整的未过滤列表),并且经过大量试验和错误(创建不再正确绑定的过滤数组等)。等)我试过了,基本上只使用原始数组。
List {
ForEach(project.tasks.filter({ $0.phase.contains(filterValue) }).indices, id: \.self) { idx in
HStack {
Text(project.tasks[idx].phase)
.font(.caption)
Spacer()
Text(project.tasks[idx].name)
Spacer()
Toggle("", isOn: self.$project.tasks[idx].isComplete)
}
}
}
当然,这似乎可行,因为我可以做一个测试:
func CreateTestArray() {
let testFilterArray = project.tasks.filter({ $0.phase.contains(filterValue) })
}
这会给我我想要的过滤列表。但是,在我的 ForEach 视图中,它无法正常工作,我不确定如何解决它。
例如,我有 128 个任务,其中 10 个的值为“Final”,当我使用按钮将 filterValue 设置为 Final 时,testFilterArray 实际上包含正确的 10 个任务 - 但在 ForEach 视图中,我获取原始数组中的前十个任务(属于“计划”类型 - 原始数组按计划/构造/最终排序);显然,尽管有 filter 语句,ForEach 仍在处理原始数组。计划按钮发送 filterValue = "Planning",我得到正确的结果,因为过滤器为我在原始数组中的 20 个计划任务返回 0-19 索引,并且由于它们是原始数组中的第一个,它'似乎'规划过滤器工作正常,但实际上它只是偶然地工作,如果数组的排序方式不同,它就不会。
有什么想法可以解决这个问题,以便我可以实际过滤这个数组,为数组中的每个项目正确显示 isComplete 切换,以及动态更新切换状态吗?我觉得我需要再次从头开始,因为我已经让这些限制让我进入了一个小小的 Swift 角落。
谢谢!
更新: 谢谢@jnpdx,您的快速回复 - 我肯定应该包含对象(我在下面列出)。然而,回顾我的对象定义,我想知道我是否在管理对象时犯了一个更基本的错误,这就是我陷入困境的原因(即,在早期的迭代中,我尝试了一些你的建议)。无论如何,我发布的对象是“项目”,它是我传递给项目列表视图的项目列表,然后该视图将单个项目传递给项目视图,然后该视图列出了该特定项目中的任务.
我觉得你的回答让我做出了正确的决定,我只需要备份并查看那些对象定义/管理,看看如何找到一个可以直接解决的情况。
任务:
struct Task: Identifiable, Codable {
let id: UUID
var phase: String
var category: String
var name: String
var isComplete: Bool
init(id: UUID = UUID(), phase: String, category: String, name: String, isComplete: Bool) {
self.id = id
self.phase = phase
self.category = category
self.name = name
self.isComplete = isComplete
}
}
项目:
struct Project: Identifiable, Codable {
var id: UUID
var name: String
var type: String
var tasks: [Task]
var isComplete: Bool
init(id: UUID = UUID(), name: String, type: String, tasks: [Task] = [], isComplete: Bool) {
self.id = id
self.name = name
self.type = type
self.tasks = tasks
self.isComplete = isComplete
}
}
和项目模型:
class ProjectData: ObservableObject {
// code to access the json file is here
// An accessible list of projects from the saved file
@Published var projects: [Project] = []
// load and save functions follow
更新: 谢谢@jnpdx,您的解决方案在按照您所说的那样进行调整以使其在我的特定模型设计中发挥作用后起作用。这是最终在我的案例中起作用的 sn-ps。
在我看来:
List {
ForEach(project.tasks.filter({ $0.phase.contains(filterValue) })) { task in
HStack {
Text(task.name)
Toggle("", isOn: self.makeBinding(item: task))
}
}
}
以及被调用的函数:
func makeBinding(item: Task) -> Binding<Bool> {
let i = self.project.tasks.firstIndex { $0.id == item.id }!
return .init(
get: { self.project.tasks[i].isComplete },
set: { self.project.tasks[i].isComplete = $0 }
)
}
【问题讨论】:
-
这里似乎缺少一些代码。你能发布
Project是什么吗? -
查看您的更新,我的所有建议都会奏效,但正如您所说,考虑到您存储数据的结构,您必须做一些工作。您当前的解决方案很好,但我的代码只需要编辑以适应它。我要说的一件事是,如果你想省略一些样板代码,你可以让编译器为你合成你的结构初始化,因为除了设置初始值之外你并没有真正做任何工作(见我的例子
Task) -
我很高兴它对你有用。我看到你是新来的,所以请考虑支持/接受我的回答
-
@jnpdx - 我想要!并尝试过,一直点击它,然后意识到弹出了一条消息 - 显然我的声誉如此之高,以至于我无法接受答案/赞成票(要求的最低声誉 = 15,我只有 11 个)。哎呀。只点击了赞成票,现在我尝试点击下面的“检查”,这可能有效。
标签: arrays filter swiftui indices