Hello everyone,
I am new to the Swift and the SwiftUI. Trying to understand how sorting works on SwiftUI Table view.
The following code does what I intend to do, but I am not able to understand how does it actually work with multiple key path. Need help with the answers for the questions posted below the code.
// MARK: - Student Model
struct Student: Codable, Identifiable {
let id: String
let name: String
let gradeHistory: GradeHistory
enum CodingKeys: String, CodingKey {
case id, name
case gradeHistory = "grade_history"
}
}
// MARK: - GradeHistory Model
struct GradeHistory: Codable, Identifiable{
let id: String?
let semester: String
let subjects: Subjects
init(
id: String? = UUID().uuidString,
semester: String,
subjects: Subjects
) {
self.id = id ?? UUID().uuidString
self.semester = semester
self.subjects = subjects
}
}
// MARK: - Subjects Model
struct Subjects: Codable, Identifiable {
let id: String?
let math: Int
let science: Int
let english: Int
let physics: Int
let computer: Int
let socialScience: Int
init(
id: String? = nil,
math: Int,
science: Int,
english: Int,
physics: Int,
computer: Int,
socialScience: Int
) {
self.id = id ?? UUID().uuidString
self.math = math
self.science = science
self.english = english
self.physics = physics
self.computer = computer
self.socialScience = socialScience
}
enum CodingKeys: String, CodingKey {
case id = "id"
case math = "Math"
case science = "Science"
case english = "English"
case physics = "Physics"
case computer = "Computer"
case socialScience = "Social Science"
}
}
let _students: [Student] = []
struct StudentGradeHistoryView: View {
@State var students = _students
@State private var sortOrder = [KeyPathComparator(\Student.name)]
var body: some View {
NavigationStack {
Table(of: Student.self,
selection: students.selectedStudents,
sortOrder: $sortOrder) {
TableColumn("Index") { student in
let index = (students.firstIndex(
where: { $0.id == student
.id }) ?? 0)
Text("No. \(index + 1)")
}
TableColumn("Id", value: \.id)
TableColumn("Name", value: \.name)
.width(min: 150)
TableColumn("Math") { student in
Text("\(student.gradeHistory.subjects.math)")
.foregroundStyle(
gradeColor(for: student.gradeHistory.subjects.math)
)
}
.defaultVisibility(.automatic)
TableColumn("Science") { student in
Text("\(student.gradeHistory.subjects.science)")
.foregroundStyle(gradeColor(for: student.gradeHistory.subjects.science))
}
TableColumn("English") { student in
Text("\(student.gradeHistory.subjects.english)")
.foregroundStyle(gradeColor(for: student.gradeHistory.subjects.english))
}
TableColumn("Physics") { student in
Text("\(student.gradeHistory.subjects.physics)")
.foregroundStyle(gradeColor(for: student.gradeHistory.subjects.physics))
}
TableColumn("Computer") { student in
Text("\(student.gradeHistory.subjects.computer)")
.foregroundStyle(gradeColor(for: student.gradeHistory.subjects.computer))
}
TableColumn("Social Science") { student in
Text("\(student.gradeHistory.subjects.socialScience)")
.foregroundStyle(gradeColor(for: student.gradeHistory.subjects.socialScience))
}
}
.onChange(of: sortOrder) {
students.sort(using: sortOrder)
}
}
}
}
My question is how I can use KeyPathComparator
in this model to sort data with multiple comparator paths like
@State private var sortOrder = [
KeyPathComparator(\Student.name),
KeyPathComparator(\Subjects.math)
]
It's not working in this way
@cp-divyesh-v I would recommend you review Building a Great Mac App with SwiftUI sample code. It has an example that walks you through how you should create sortable columns.
However here's an example :
struct Plant: Identifiable {
var id = UUID()
var variety: String
var daysToMaturity: Int
var datePlanted: Date
var harvestDate: Date
var lastWateredOn: Date
var favorite: Bool
}
@Observable
class DetailManager {
var sortOrder: [KeyPathComparator<Plant>] = [
.init(\.daysToMaturity, order: SortOrder.reverse),
.init(\.variety, order: SortOrder.reverse)
]
var data = [
Plant(variety: "Tomato", daysToMaturity: 80, datePlanted: Date(), harvestDate: Date(), lastWateredOn: Date(), favorite: true),
Plant(variety: "Carrot", daysToMaturity: 60, datePlanted: Date(), harvestDate: Date(), lastWateredOn: Date(), favorite: false),
Plant(variety: "Pepper", daysToMaturity: 90, datePlanted: Date(), harvestDate: Date(), lastWateredOn: Date(), favorite: false)
]
var plants: [Plant] {
data.sorted(using: sortOrder)
}
}
struct GardenDetailTable: View {
@State private var selection = Set<Plant.ID>()
@State private var viewModel = DetailManager()
var body: some View {
Table(selection: $selection, sortOrder: $viewModel.sortOrder) {
TableColumn("Variety", value: \.variety)
TableColumn("Days to Maturity", value: \.daysToMaturity) { plant in
Text(plant.daysToMaturity.formatted())
}
TableColumn("Date Planted", value: \.datePlanted) { plant in
Text(plant.datePlanted.formatted(date: .abbreviated, time: .omitted))
}
TableColumn("Harvest Date", value: \.harvestDate) { plant in
Text(plant.harvestDate.formatted(date: .abbreviated, time: .omitted))
}
TableColumn("Last Watered", value: \.lastWateredOn) { plant in
Text(plant.lastWateredOn.formatted(date: .abbreviated, time: .omitted))
}
} rows: {
ForEach(viewModel.plants) { plant in
TableRow(plant)
}
}
}
}