SwiftUI - 详细视图更改为 @fetchRequest 弹出导航链接视图

时间:2020-12-29 21:25:24

标签: ios swift swiftui

我有两种看法。一个视图是使用@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()
        }     
    }
}

1 个答案:

答案 0 :(得分:2)

正如您所指出的,NavigationLink 被弹出是因为从获取请求中删除了底层模型。不幸的是,这意味着您必须将导航链接移到列表视图之外,以便您可以缓存模型,即使它从获取结果列表中删除。

一种方法是将您的 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")
                                }
                            })
                        }
                    }
                }
          }
      }
}