在SwiftUI中对核心数据使用@FetchRequest时修改nil排序行为

时间:2020-02-23 19:17:42

标签: ios swift core-data swiftui

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

  1. 如果有下一个约会,请对它们进行升序排序
  2. 在下一个日期的下方,按创建日期对其余日期进行排序

我有:

@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(比较器块不 支持)”

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

2 个答案:

答案 0 :(得分:1)

@FetchRequest将排序描述符用作获取请求的一部分,因此,您不能在获取请求的定义中使用比较器块(至少,不适用于SQLite存储)。但是,您可以使用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...
                ...
            }
        }
    }
}

为简单起见,我假设dateCreated是非可选的。如果不是,则需要修改谓词以处理nil值。也可能会写得更简洁一些-但我希望您能明白。

仅需注意其在内存中的排序,因此不知道此解决方案的性能和/或内存影响。

答案 1 :(得分:0)

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

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

您可以设置Special Date而不是“ Nil”日期,如果所需数据为“ Nil”,那将很晚。通过这种方式,您可以将这些日期排序到底部。 您可能不会显示这些特殊日期,也不会在SwiftView中将其显示为“无”。

您只能玩NSString游戏,不能玩subclass