【问题标题】:Sort NSSortDescriptor by Month of date. Swift按月份对 NSSortDescriptor 进行排序。迅速
【发布时间】:2020-10-06 06:30:37
【问题描述】:

我正在显示生日列表。 我想以从一月开始到十二月结束的方式对它们进行排序,第二个 NSSortDescriptor 是一天。我想显示一个列表,其中最接近的生日首先出现。

目前它按完整的生日日期排序。因此,目前“2009 年 2 月 6 日”显示在“1997 年 2 月 8 日”之后,我不希望它显示,因为当想知道下一个生日何时到来时,年份无关紧要。

我的 FetchRequest 是什么样子的:

    @FetchRequest(entity: Birthday.entity(), sortDescriptors: [
    NSSortDescriptor(keyPath: \Birthday.date, ascending: true),

]) var birthdays: FetchedResults<Birthday>

我的列表是什么样的:

                List {
                ForEach(birthdays, id: \.self) { birthday in
                    ZStack {
                        NavigationLink(destination: DetailView(birthday: birthday)) {
                            EmptyView()

                        }.hidden()

                        BirthdayView(name: birthday.name ?? "Unknown Name", birthDate: birthday.date ?? Date())

                    }
                }
                .onDelete(perform: deleteBirthdays)
            }

我正在使用 Core Data,这就是我的实体的样子:

使用最新的 XCODE 和 Swift。

提前致谢!

我试过了:

extension Date {
var isInToday: Bool { Calendar.current.isDateInToday(self) }
var nextBirthday: Date {
    isInToday ? self : Calendar.current.nextDate(after: Date(), matching: Calendar.current.dateComponents([.month, .day, .hour, .minute], from: self), matchingPolicy: .nextTime)!
}

}

extension Birthday {
var nextBirthday: Date { date!.nextBirthday }

}

@FetchRequest(entity: Birthday.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Birthday.nextBirthday, ascending: true)
]) var birthdays: FetchedResults<Birthday>

但是在运行应用程序时它会抛出这显示以下错误:

“线程 1:致命错误:无法从 KeyPath Swift.KeyPath 中提取字符串”

【问题讨论】:

  • 谢谢我试过了,但它现在给出了这个错误:“表达式类型不明确,没有更多上下文”

标签: ios swift iphone core-data swiftui


【解决方案1】:

如果您拥有(或将拥有)大型数据库,那么在数据模型中设置专用属性(例如,birthMonth: String,具有“MM/DD”之类的内容)并让 CoreData 排序引擎进行排序是合适的通过Birthday.birthMonth 键路径为您提供。

否则,在小型数据库的情况下,您可以在获取数据后立即采取其他措施,如

ForEach(birthdays.sorted { $0.month < $1.month }, id: \.self) { birthday in

通过相应的.month 扩展实现以满足您的需求。

【讨论】:

  • 'something like "Sep, 6"' 会按字母顺序排序。 OP需要的是“MM/dd”。要考虑的另一件事是实际日期,以便过去的生日(上个月或上周)应该到最后
【解决方案2】:

一个可能的解决方案是 NSDate 的扩展(NSDate 至关重要,因为 Core Data 广泛基于 NSObject-KVO)。

不管年份如何,它都会计算下一次出现的日期

extension NSDate {

    dynamic var nextOccurrence : NSDate {
        let calendar = Calendar.current
        let components = calendar.dateComponents([.month, .day], from: self as Date)
        return calendar.nextDate(after: Date(), matching: components, matchingPolicy: .nextTime)! as NSDate
    }
}

对应的排序描述符是

NSSortDescriptor(key: "date.nextOccurrence", ascending: true)

【讨论】:

  • 这不会考虑当前日期
  • 我不明白。
  • OP 想对下一个生日进行排序。这会将今年已经发生的生日排序在仍然到来的生日之前。 “知道下一个生日何时到来。”
  • 有点模棱两可:我想以从一月开始到十二月结束的方式对它们进行排序
  • 您需要更改此属性以返回从今天到下一个生日的天数
【解决方案3】:

您可以创建一个自定义排序,让您的日期按传入的生日排序,并将排序后的集合传递给您的 ForEach 方法:

extension Collection where Element == Birthday {
    func sortedByIncomingBirthday() -> [Element] {
        sorted {
            // lhs date is today or is in the future
            ($0.date.month, $0.date.day) >= (Date().month, Date().day) ?
                // rhs date is today or is in the future
                ($1.date.month, $1.date.day) >= (Date().month, Date().day) ?
                    // sort them by a month day order
                    ($0.date.month, $0.date.day) < ($1.date.month, $1.date.day) :
                    // otherwise is in increasing order
                    true :
            // lhs date is in the past
            // sort them by a month day order
            ($0.date.month, $0.date.day) < ($1.date.month, $1.date.day)
        }
    }
}

ForEach(birthdays.sortedByIncomingBirthday(), id: \.self) { birthday in

另一种选择是扩展生日并添加一个 nextBirthday 属性,您可以使用它作为 NSSortDescriptor 的 keyPath 对其进行排序:

extension Date {
    var isInToday: Bool { Calendar.current.isDateInToday(self) }
    var nextBirthday: Date {
        isInToday ? self : Calendar.current.nextDate(after: Date(), matching: Calendar.current.dateComponents([.month, .day, .hour, .minute], from: self), matchingPolicy: .nextTime)!
    }
}

extension Birthday {
    var nextBirthday: Date { date.nextBirthday }
}

NSSortDescriptor(keyPath: \Birthday.nextBirthday, ascending: true)

编辑/更新:

如果生日日期属性是可选的,不要强制解包它的值:

extension Birthday {
    var nextBirthday: Date { date?.nextBirthday ?? .distantFuture }
}

第一种方法也需要处理它:

extension Collection where Element == Birthday {
    func sortedByIncomingBirthday() -> [Element] {
        sorted { lhs, rhs in
            if lhs.date == nil { return false }
            let lhs = lhs.date ?? .distantFuture
            let rhs = rhs.date ?? .distantFuture
            // lhs date is today or is in the future
            return (lhs.month, lhs.day) >= (Date().month, Date().day) ?
                       // rhs date is today or is in the future
                       (rhs.month, rhs.day) >= (Date().month, Date().day) ?
                           // sort them by a month day order
                           (lhs.month, lhs.day) < (rhs.month, rhs.day) :
                           // otherwise is in increasing order
                           true :
                   // lhs date is in the past
                   // sort them by a month day order
                   (lhs.month, lhs.day) < (rhs.month, rhs.day)
        }
    }
}

【讨论】:

  • 实现这个会产生以下错误:在“Collection”上引用实例方法“sortedByIncomingBirthday()”需要类型“FetchedResults.Element”(又名“Birthday”)和“Date”是等效的在ForEach(birthdays.sortedByIncomingBirthday(), id: \.self) { birthday in
  • 是的,它在 NSSortDescriptor 行出现以下错误:线程 1:致命错误:无法从 KeyPath Swift.KeyPath 中提取字符串
  • 第一种方法呢?
  • @Shackkill 你能编辑你的帖子并展示你的尝试吗?
  • @Shackkill 不强制解包日期。试试var nextBirthday: Date { date?.nextBirthday ?? .distantFuture }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
相关资源
最近更新 更多