简体   繁体   English

在 SwiftUI 中使用 @FetchRequest 和 Core Data 时修改 nil 排序行为

[英]Modify nil sort behavior when using @FetchRequest with Core Data in SwiftUI

I'm fetching entities from Core Data in SwiftUI and would like to sort them like so:我正在从 SwiftUI 中的核心数据中获取实体,并希望像这样对它们进行排序:

  1. If there's a next date, sort them ascending如果有下一个日期,将它们升序排序
  2. Underneath those that have a next date, sort the remaining by date created在有下一个日期的下方,按创建日期对剩余的进行排序

I have:我有:

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

The problem is those that do not have a next date are all sorted above those that do have a non-nil next date, as seen in this other question .问题是那些没有下一个日期的人都排在那些下一个日期非零的人之上,如 this other question中所示。

I tried to subclass NSSortDescriptor to move nil values to the end, but it never calls any of the overridden functions.我试图子类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)
    }
}

I also tried to use the NSSortDescriptor API that includes a comparator block, however this crashes with我还尝试使用包含比较器块的NSSortDescriptor API,但是这会崩溃

Exception: "unsupported NSSortDescriptor (comparator blocks are not supported)"异常:“不支持的 NSSortDescriptor(不支持比较器块)”

How could I achieve the desired sort behavior?我怎样才能实现所需的排序行为?

The @FetchRequest uses the sort descriptor as part of a fetch request, and consequently you cannot use comparator blocks in the definition of the fetch request (at least, not with an SQLite store). @FetchRequest 使用排序描述符作为获取请求的一部分,因此您不能在获取请求的定义中使用比较器块(至少不能使用 SQLite 存储)。 However, you can sort the results of the fetch ( entities ) as part of the processing of the body of the view, using sorted(by:) :但是,您可以使用sorted(by:)对获取( 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...
                ...
            }
        }
    }
}

I've assumed for simplicity that dateCreated is non-optional.为简单起见,我假设dateCreated是非可选的。 If not, you will need to amend the predicate for the sort to handle the nil values.如果不是,您将需要修改排序的谓词以处理 nil 值。 It could probably be written more prettily, too - but I hope you get the idea.它可能也可以写得更漂亮 - 但我希望你明白了。

Just to note it's sorting in memory, so no idea of performance and/or memory impact of this solution.请注意它在 memory 中排序,因此不知道此解决方案的性能和/或 memory 影响。

In CoreData, sorting is only using method from NSString class like "compare:" or "localizedStandardCompare:" something like those.在 CoreData 中,排序仅使用 NSString class 中的方法,例如“compare:”或“localizedStandardCompare:”之类的方法。

As you cannot override those methods or even change them in most cases, you may consider other workarounds.由于在大多数情况下您无法覆盖这些方法甚至无法更改它们,因此您可以考虑其他解决方法。

Instead of "Nil" date, you can set a Special Date which is very late if the desired data is 'Nil'.如果所需数据为“无”,您可以设置一个非常晚的Special Date ,而不是“无”日期。 In this way you can sort those dates into the bottom.通过这种方式,您可以将这些日期排序到底部。 You may not display those special dates, or display them as "Nil" in SwiftView.您可能不会显示这些特殊日期,或在 SwiftView 中将它们显示为“Nil”。

You can just play NSString game, not the subclass你可以只玩NSString游戏,而不是subclass

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM