在 SwiftUI 中将 @FetchRequest 与 Core Data 一起使用时修改 nil 排序行为

Posted

技术标签:

【中文标题】在 SwiftUI 中将 @FetchRequest 与 Core Data 一起使用时修改 nil 排序行为【英文标题】:Modify nil sort behavior when using @FetchRequest with Core Data in SwiftUI 【发布时间】:2020-02-23 19:17:42 【问题描述】:

我正在 SwiftUI 中从 Core Data 中获取实体,并希望像这样对它们进行排序:

    如果有下一个日期,按升序排序 在那些有下一个日期的下方,按创建日期对剩余的进行排序

我有:

@FetchRequest(entity: MyEntity.entity(), sortDescriptors: [
    NSSortDescriptor(keyPath: \MyEntity.nextDate, ascending: true),
    NSSortDescriptor(keyPath: \MyEntity.dateCreated, ascending: true)
]) private var entities: FetchedResults<MyEntity>

问题是那些没有下一个日期的人都排在那些有非零下一个日期的人之上,如other question所示。

我尝试继承 NSSortDescriptor 以将 nil 值移动到末尾,但它从不调用任何被覆盖的函数。

final class NilsLastSortDescriptor: NSSortDescriptor 
    override func copy(with zone: NSZone? = nil) -> Any 
        return NilsLastSortDescriptor(key: self.key, ascending: self.ascending, selector: self.selector)
    

    override var reversedSortDescriptor: Any 
        return NilsLastSortDescriptor(key: self.key, ascending: !self.ascending, selector: self.selector)
    

    override func compare(_ object1: Any, to object2: Any) -> ComparisonResult 
        if (object1 as AnyObject).value(forKey: self.key!) == nil && (object2 as AnyObject).value(forKey: self.key!) == nil 
            return .orderedSame
        
        if (object1 as AnyObject).value(forKey: self.key!) == nil 
            return .orderedDescending
        
        if (object2 as AnyObject).value(forKey: self.key!) == nil 
            return .orderedAscending
        
        return super.compare(object1, to: object2)
    

我也尝试使用包含比较器块的NSSortDescriptor API,但是这会崩溃

例外:“不支持的 NSSortDescriptor(比较器块不是 支持)”

如何实现所需的排序行为?

【问题讨论】:

请展示您如何在@FetchRequest 中调用子类 NSSortDescriptor @E.Coms 更新 【参考方案1】:

@FetchRequest 使用排序描述符作为获取请求的一部分,因此您不能在获取请求的定义中使用比较器块(至少不能使用 SQLite 存储)。但是,您可以使用 sorted(by:) 对 fetch (entities) 的结果进行排序,作为视图的 body 处理的一部分:

var body: some View 
        VStack 
        List
            ForEach(self.entities.sorted(by:  first, second in
                if first.nextDate == nil && second.nextDate == nil  return (first.dateCreated <= second.dateCreated) 
                if first.nextDate == nil  return false 
                if second.nextDate == nil  return true 
                if first.nextDate! == second.nextDate!  return (first.dateCreated <= second.dateCreated) 
                return (first.nextDate! < second.nextDate!)
            ), id: \.self)  myEntity in
                // Your code for your cells...
                ...
            
        
    

为了简单起见,我假设dateCreated 是非可选的。如果不是,您将需要修改排序谓词以处理 nil 值。它也可能写得更漂亮 - 但我希望你能明白。

只是要注意它是在内存中排序的,因此不知道此解决方案的性能和/或内存影响。

【讨论】:

【参考方案2】:

在 CoreData 中,排序仅使用 NSString 类中的方法,例如“compare:”或“localizedStandardCompare:”之类的。

由于在大多数情况下您无法覆盖这些方法,甚至无法更改它们,您可以考虑其他解决方法。

您可以设置Special Date,而不是“Nil”日期,如果所需数据为“Nil”,则该日期非常晚。通过这种方式,您可以将这些日期排序到底部。 您可能不会显示这些特殊日期,或者在 SwiftView 中将它们显示为“Nil”。

您可以只玩NSString 游戏,而不是subclass

【讨论】:

以上是关于在 SwiftUI 中将 @FetchRequest 与 Core Data 一起使用时修改 nil 排序行为的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中将 JSON 数据作为对象加载

如何在swiftui中将vstack移动到屏幕顶部?

在 SwiftUI 中将部分文本加粗

如何在swiftUI中将背景颜色添加到列表中

如何在 SwiftUI 中将 TabView 与 NavigationView 一起使用?

如何在 SwiftUI 中将 LocalizedStringKey 更改为 String