ios - SwiftUI - 详细视图更改为 @fetchRequest 弹出导航链接视图
问题描述
我有两种看法。一种视图是使用@fetchRequest 从核心数据存储中获取的对象(人员)列表。另一个视图是详细视图,用户可以通过点击列表视图中的人员项目导航到该视图。这个想法是用户可以在详细视图中编辑一个人的详细信息(例如姓名、年龄)。到目前为止,一切都很好。
关键点是列表视图的设计使得并非所有人都必须从核心数据存储中获取。仅提取满足特定条件的人。让我们假设标准是这个人必须在 30 到 40 岁之间。
我的问题是,当用户将详细视图中的人的年龄更改为不符合获取请求标准的某个年龄时(例如,他将年龄从 35 岁更改为 20 岁),详细视图将在用户点击保存按钮,托管对象上下文保存,这是由@fetchRequest 在列表视图中注册的。
我知道会发生这种情况,因为驱动列表视图的人员的 fetchRequest 发生了变化,因为编辑的人被删除了,因为他不再满足 30 到 40 年的要求。但我不希望在用户仍在详细视图中时发生这种情况。点击保存按钮不应自动弹出详细视图。在使用@fetchRequest 时有什么方法可以防止这种情况发生吗?
这是导致所描述问题的代码的精简版本:
struct PersonList: View {
@FetchRequest var persons: FetchedResults<Person>
init() {
let lowerAge = NSPredicate(format: "%K >= %@", #keyPath(Person.age), 30 as CVarArg)
let upperAge = NSPredicate(format: "%K <= %@", #keyPath(Person.age), 40 as CVarArg)
let agePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [lowerAge, upperAge])
_persons = FetchRequest(entity: Person.entity(), sortDescriptors: [], predicate: agePredicate)
}
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
LazyVStack(spacing: 10) {
ForEach(persons, id: \.id) { person in
NavigationLink(destination: PersonDetail(person: person)) {
Text(person.name)
}
}
}
}
}
}
struct PersonDetail: View {
@Environment(\.managedObjectContext) var viewContext
@State var ageInput: String = ""
var person: Person
var body: some View {
TextField("Enter Age", text: $ageInput)
.onAppear(perform: {
ageInput = "\(person.age)"
})
Button("Save", action: { save() } )
}
}
func save() {
if let age = Int(ageInput) {
viewContext.saveContext()
}
}
}
解决方案
正如您所指出的,NavigationLink
因为从 fetch 请求中删除了底层模型,所以会弹出。不幸的是,这意味着您必须将导航链接移到列表视图之外,以便即使模型从获取结果列表中删除,您也可以缓存模型。
一种方法是将您的 VStack/ScrollView 嵌入到 ZStack 中,并带有一个隐藏的“单例”导航链接和一个跟踪主动选择的人的@State 变量:
struct PersonList: View {
@FetchRequest var persons: FetchedResults<Person>
@State private var selectedPerson = Person(entity: Person.entity(), insertInto: nil)
@State private var showPersonDetail = false
var body: some View {
NavigationView {
ZStack {
NavigationLink(destination: PersonDetail(person: selectedPerson), isActive: $showPersonDetail) { EmptyView() }.hidden()
ScrollView(showsIndicators: false) {
LazyVStack(spacing: 10) {
ForEach(persons, id: \.id) { person in
Button(action: {
selectedPerson = person
showPersonDetail = true
}, label: {
// You could hack out the click/tap listener for a navigation link instead of
// emulating its appearance, but that hack could stop working in a future iOS release.
HStack {
Text(person.name)
Spacer()
Image(systemName: "chevron.right")
}
})
}
}
}
}
}
}
推荐阅读
- scala - 在使用 scalatest 读取 json 字符串时,我们如何编写测试用例来处理格式错误的数据/解析错误
- wpf - 自动但拉伸的WPF网格列宽度
- javascript - 使用 Lodash 按键将一个对象嵌套到另一个对象中
- katalon-studio - 比较 Katalon Studio 中的两个 API 响应
- c# - 如何将 UI 元素始终放在 UWP 中的顶部
- java - 谷歌地图未显示在我的应用程序中,并出现在另一个应用程序中
- php - 列数与值不匹配
- npm - 创建和发布的版本之间的 Github Action 不同
- android - Kotlin - 如何在自定义警报对话框中保存复选框的状态?
- python - Python pyodbc 重复键被忽略