@FetchRequest 中的 SwiftUI 和动态 NSSortDescriptors

Posted

技术标签:

【中文标题】@FetchRequest 中的 SwiftUI 和动态 NSSortDescriptors【英文标题】:SwiftUI and dynamic NSSortDescriptors in @FetchRequest 【发布时间】:2020-09-01 12:31:26 【问题描述】:

我有一个包含标题和日期的项目列表。用户可以设置排序依据(标题或日期)。

但是我不知道如何动态更改 NSSortDescriptor。

import SwiftUI
import CoreData

struct ContentView: View 
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Test.title, ascending: true)], animation: .default) private var items: FetchedResults<Test>

    @State private var sortType: Int = 0
    @State private var sortDescriptor: NSSortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)

    var body: some View 
        Picker(selection: $sortType, label: Text("Sort")) 
            Text("Title").tag(0)
            Text("Date").tag(1)
        
        .pickerStyle(SegmentedPickerStyle())
        .onChange(of: sortType)  value in
            sortType = value

            if sortType == 0 
                sortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
             else 
                sortDescriptor = NSSortDescriptor(keyPath: \Test.date, ascending: true)
            
        

        List 
            ForEach(items)  item in
                let dateString = itemFormatter.string(from: item.date!)
                HStack 
                    Text(item.title!)
                    Spacer()
                    Text(dateString)
                
            
        

        .onAppear(perform: 
            if items.isEmpty 
                let newEntry1 = Test(context: self.viewContext)
                newEntry1.title = "Apple"
                newEntry1.date = Date(timeIntervalSince1970: 197200800)

                let newEntry2 = Test(context: self.viewContext)
                newEntry2.title = "Microsoft"
                newEntry2.date = Date(timeIntervalSince1970: 168429600)

                let newEntry3 = Test(context: self.viewContext)
                newEntry3.title = "Google"
                newEntry3.date = Date(timeIntervalSince1970: 904903200)

                let newEntry4 = Test(context: self.viewContext)
                newEntry4.title = "Amazon"
                newEntry4.date = Date(timeIntervalSince1970: 773402400)

                if self.viewContext.hasChanges 
                    try? self.viewContext.save()
                
            
        )
    


private let itemFormatter: DateFormatter = 
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .none
    return formatter
()

当我改变时

@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Test.title, ascending: true)], animation: .default) private var items: FetchedResults&lt;Test&gt;

@FetchRequest(sortDescriptors: [sortDescriptor], animation: .default) private var items: FetchedResults&lt;Test&gt;

出现错误“不能在属性初始化器中使用实例成员'sortDescriptor';属性初始化器在'self'可用之前运行”。

我还尝试将 NSSortDescriptor 存储在 UserDefault 中并创建一个 init 来创建它自己的 FetchRequest.. 仍然没有动态排序...

有人指点在哪里寻找解决此问题的方法吗?

在这里找到整个项目:https://github.com/l1ghthouse/FRDSD

【问题讨论】:

在这个答案***.com/a/59345830/12299030 中考虑方法 - 它也适用于描述符。 @Asperi 是的,这行得通...非常感谢! 【参考方案1】:

解决了!感谢 Asperi 指出这个 QA:https://***.com/a/59345830/12299030

import SwiftUI
import CoreData

struct ContentView: View 
    @Environment(\.managedObjectContext) private var viewContext

    @AppStorage("firstLaunch") var firstLaunch: Bool = true
    @State var sortDescriptor: NSSortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
    @State private var sortType: Int = 0

    var body: some View 
        Picker(selection: $sortType, label: Text("Sort")) 
            Text("Title").tag(0)
            Text("Date").tag(1)
        
        .pickerStyle(SegmentedPickerStyle())
        .onChange(of: sortType)  value in
            sortType = value

            if sortType == 0 
                sortDescriptor = NSSortDescriptor(keyPath: \Test.title, ascending: true)
             else 
                sortDescriptor = NSSortDescriptor(keyPath: \Test.date, ascending: true)
            
        

        ListView(sortDescripter: sortDescriptor)

        .onAppear(perform: 
            if firstLaunch == true 
                let newEntry1 = Test(context: self.viewContext)
                newEntry1.title = "Apple"
                newEntry1.date = Date(timeIntervalSince1970: 197200800)

                let newEntry2 = Test(context: self.viewContext)
                newEntry2.title = "Microsoft"
                newEntry2.date = Date(timeIntervalSince1970: 168429600)

                let newEntry3 = Test(context: self.viewContext)
                newEntry3.title = "Google"
                newEntry3.date = Date(timeIntervalSince1970: 904903200)

                let newEntry4 = Test(context: self.viewContext)
                newEntry4.title = "Amazon"
                newEntry4.date = Date(timeIntervalSince1970: 773402400)

                if self.viewContext.hasChanges 
                    try? self.viewContext.save()
                

                firstLaunch = false
            
        )
    


struct ListView: View 
    @FetchRequest var items: FetchedResults<Test>
    @Environment(\.managedObjectContext) var viewContext

    init(sortDescripter: NSSortDescriptor) 
        let request: NSFetchRequest<Test> = Test.fetchRequest()
        request.sortDescriptors = [sortDescripter]
        _items = FetchRequest<Test>(fetchRequest: request)
    

    var body: some View 
        List 
            ForEach(items)  item in
                let dateString = itemFormatter.string(from: item.date!)
                HStack 
                    Text(item.title!)
                    Spacer()
                    Text(dateString)
                
            
        
    


private let itemFormatter: DateFormatter = 
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .none
    return formatter
()

【讨论】:

你能让动画在这个上工作吗?当我排序时,整个列表视图被重新创建并且没有重新排序动画。

以上是关于@FetchRequest 中的 SwiftUI 和动态 NSSortDescriptors的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中限制 FetchRequest 中的结果数

当SwiftUI中的相关实体发生变化时,如何更新@FetchRequest?

在@FetchRequest 中输入一个动态值,以从 SwiftUI 中的核心数据中获取单个实体

SwiftUI - 使用 @ObservedObject 作为同一结构中 @FetchRequest 的结果来强制更新 UI

SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据

SwiftUI - 如何在 init 中使用 fetchRequest 更新数据