修改@FetchRequest

时间:2019-08-09 08:31:48

标签: swiftui

Xcode beta 5为SwiftUI引入了@FetchRequest

我有一个视图,其中有一个@FetchRequestNSFetchRequest是在管理器中创建的,该管理器使属性包装器可以使用其自身的静态实例(它们不能使用self)。创建时将同一实例传递给视图。

我想要的是在调用FetchRequest时更新self.manager.reverse.toggle(),以便视图改变其对象顺序。不幸的是,即使我创建新对象并在不同视图之间进行转换,似乎Manager.fetchRequest()也仅被调用一次,然后再也不会调用。

我正在寻找有关如何修改使用新属性包装程序提出的提取请求的建议,以便可以根据用户操作对对象进行重新处理。

struct MainView: some View {
    @ObservedObject var manager: Manager

    @FetchRequest(fetchRequest: Manager.shared.fetchRequest())
    var ts: FetchedResults

    var body: some View {
        NavigationView {
            List(ts, id: \.self) { t in
                Text(t.name)
            }
        }.navigationBarItems(trailing:
            Button(action: { 
                self.manager.reverse.toggle() 
            }) { Text("Reverse")}
    }
}

final class Manager: ObservableObject {
    @Published var reverse: Bool = false

    // Since property wrappers cannot use self, we make a shared instance
    // available statically. This same instance is also given to the view. 
    public static let shared = Manager()

    func fetchRequest(reverse: Bool = false) -> NSFetchRequest<T> {
        let request: NSFetchRequest<T> = T.fetchRequest()

        request.sortDescriptors = [
            NSSortDescriptor(
                key: "name",
                ascending: !self.reverse
            )
        ]

        return request
    }
}

3 个答案:

答案 0 :(得分:3)

这是一个很好的问题!我一直在尝试执行此操作以及其他非常相似的操作,但收效甚微。我真的希望这个问题在即将发布的Beta中能够得到适当的解决。但是,与此同时,可以在两个@FetchRequests之间切换以完成您想要做的事情。

以下是我正在处理的示例应用程序中的工作代码。我使用Button来切换布尔值@State,然后使用该状态选择适当的@FetchResults。我不太喜欢,但实际上效果很好。

import SwiftUI
import CoreData

struct EntityListView : View {

    @Environment(\.editMode) var editMode

    @State var sortAscending: Bool = true

    @Environment(\.managedObjectContext) var context: NSManagedObjectContext

    @FetchRequest(entity: Entity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Entity.order, ascending: true)])
    var ascendingEntities: FetchedResults<Entity>

    @FetchRequest(entity: Entity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Entity.order, ascending: false)])
    var descendingEntities: FetchedResults<Entity>

    var body: some View {

        NavigationView {
            List() {
                Section(header: Text("All Entities".uppercased()))
                {
                    ForEach(sortAscending ? ascendingEntities : descendingEntities) { entity in

                            NavigationLink(destination: EntityEditView(entity: entity)) {

                                HStack {
                                    Text(entity.name)
                                        .font(.headline)
                                    Spacer()
                                    Text(String(entity.order))
                                        .font(.subheadline)
                                        .foregroundColor(.gray)
                                }
                            }
                            }
                            .onMove(perform: self.move)
                            .onDelete(perform: self.delete)
                }

                HStack {
                    Spacer()
                    Button(action: { self.sortAscending.toggle() }) { Text("Reverse Sort") }
                    Spacer()
                }
            }
            .listStyle(GroupedListStyle())
            .navigationBarTitle(Text("Entities"), displayMode: .large)
            .navigationBarItems(trailing: EditButton() )
        }
    }
}

答案 1 :(得分:0)

SwiftUI使用不可变的结构,这意味着无法更改获取请求属性。您需要将sort参数传递给struct init,从而在要更改它时创建一个全新的结构。

我在这里学到了这一点: How to fix “Cannot assign to property: 'self' is immutable”

SwiftUI的视图应为结构,这意味着它们是不可变的 默认。如果这是我们自己的代码,我们可以使用 变异以告知Swift他们将更改值,但是我们不能这样做 在SwiftUI中,因为它使用计算属性。

答案 2 :(得分:0)

要解决您的问题,您应该将MainView包裹在SuperView中。超级视图应该观察经理。当经理变更时,将调用超级视图的 body并创建MainView的新实例。创建该实例时,您将获得一个新的@FeatchRequest。

struct SuperView: some View {
    @ObservedObject var manager: Manager

    var body: some View {
       MainView()
    }
}

我希望它能工作,但我无法测试代码。