Reputation: 33
I have a struct like this:
import Foundation
struct Product: Identifiable {
var name: String
let expirationDate: Date
let id = UUID()
}
and a list like this:
import SwiftUI
struct ContentView: View {
@Binding var products: [Product]
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}
var body: some View {
VStack {
List {
ForEach(products) { product in
HStack {
Text(product.name)
Spacer()
Text(self.dateFormatter.string(from: product.expirationDate))
}
}
}
}
how can I sort this list so that the closest expiration date from now is on top of the list?
I don't even know how to get just the expirationDates from products array. I would appreciate any help. Thank you in advance!
Upvotes: 1
Views: 1064
Reputation: 30726
They recently added Table
that gives you some sort functionality. The docs say to use onChange
but I prefer to use a struct with didSet
which prevents body being recomputed twice and also allows an initial sort. e.g.
import SwiftUI
struct Product: Identifiable {
let id = UUID()
let name: String
let expirationDate: Date
}
var myProducts: [Product] = [Product(name: "Car", expirationDate: Date.now.advanced(by: 5400)), Product(name: "Boat", expirationDate: Date.now)]
struct ProductsData {
var sortedProducts: [Product]
var sortOrder = [KeyPathComparator(\Product.name)] {
didSet {
sortedProducts.sort(using: sortOrder)
}
}
init() {
sortedProducts = myProducts.sorted(using: sortOrder)
}
}
struct ContentView: View {
@State private var data = ProductsData()
var body: some View {
ProductsTable(data: $data)
}
}
struct ProductsTable: View {
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
@Binding var data: ProductsData
var body: some View {
Table(data.sortedProducts, sortOrder: $data.sortOrder) {
TableColumn("Name", sortUsing: KeyPathComparator(\Product.name)) { product in
if horizontalSizeClass == .compact {
LabeledContent {
Text(product.expirationDate, format: .dateTime)
} label: {
Text(product.name)
}
} else {
Text(product.name)
}
}
TableColumn("Expiration Date", sortUsing: KeyPathComparator(\Product.expirationDate)) { product in
Text(product.expirationDate, format: .dateTime)
}
}
}
}
Upvotes: 2
Reputation: 5104
You need to sort your array based on timeIntervalSinceNow
:
$0.expirationDate.timeIntervalSinceNow < $1.expirationDate.timeIntervalSinceNow
Edit(Thanks to Martin R) You can compare Date
directly:
$0.expirationDate < $1.expirationDate
So in your view, ForEach(products)
becomes:
Foreach(products.sorted(by: {$0.expirationDate < $1.expirationDate}))
Upvotes: 3