| Code | What It Does | How It Does It |
| ▶ IMPORTS | | |
| import SwiftUI | Framework imports | Imports SwiftUI. |
| ▶ GROUP MODEL (FILE-PRIVATE SO GROUPHEADER CAN REFERENCE IT) | | |
| private struct FoodGroup: Identifiable {
enum Kind { case priority(Int), star(Int) }
let id: String
let kind: Kind
let entries: [WatchEntry]
} | `FoodGroup` struct | Defines the `FoodGroup` struct. Conforms to Identifiable. |
| struct WatchFoodView: View {
@EnvironmentObject var store: WatchEntryStore
// MARK: - Data
private var food: [WatchEntry] {
store.entries.filter { $0.entryMode == .food }
}
private var groups: [FoodGroup] {
var result: [FoodGroup] = []
for level in stride(from: 5, through: 1, by: -1) {
let p = food
.filter { !$0.beenThere && $0.rating == level }
.sorted { $0.playName.lowercased() < $1.playName.lowercased() }
if !p.isEmpty {
result.append(FoodGroup(id: "p\(level)", kind: .priority(level), entries: p))
}
let s = food
.filter { $0.beenThere && $0.starRating == level }
.sorted { $0.playName.lowercased() < $1.playName.lowercased() }
if !s.isEmpty {
result.append(FoodGroup(id: "s\(level)", kind: .star(level), entries: s))
}
}
// Unrated to-try
let unp = food
.filter { !$0.beenThere && $0.rating == 0 }
.sorted { $0.playName.lowercased() < $1.playName.lowercased() }
if !unp.isEmpty {
result.append(FoodGroup(id: "p0", kind: .priority(0), entries: unp))
}
// Unrated been
let uns = food
.filter { $0.beenThere && $0.starRating == 0 }
.sorted { $0.playName.lowercased() < $1.playName.lowercased() }
if !uns.isEmpty {
result.append(FoodGroup(id: "s0", kind: .star(0), entries: uns))
}
return result
}
// MARK: - Body
var body: some View {
Group {
if !store.isLoaded {
ProgressView()
} else if groups.isEmpty {
VStack(spacing: 8) {
Image(systemName: "fork.knife")
.font(.system(size: 28))
.foregroundColor(.green)
Text("No food yet")
.font(.caption)
.foregroundColor(.secondary)
}
} else {
List {
ForEach(groups) { group in
Section {
ForEach(group.entries) { entry in
NavigationLink(destination: WatchEntryDetailView(entry: entry)) {
FoodRow(entry: entry)
}
}
} header: {
GroupHeader(kind: group.kind)
}
}
}
.listStyle(.plain)
.environment(\.defaultMinListRowHeight, 0)
}
}
.navigationTitle("Food")
.navigationBarTitleDisplayMode(.inline)
}
} | `WatchFoodView` struct | Defines the `WatchFoodView` struct. Conforms to View. |
| ▶ GROUP HEADER | | |
| private struct GroupHeader: View {
let kind: FoodGroup.Kind
var body: some View {
switch kind {
case .priority(let level):
HStack(spacing: 3) {
if level == 0 {
Text("Unrated")
.font(.system(size: 10, weight: .semibold))
.foregroundColor(.secondary)
} else {
ForEach(1...5, id: \.self) { dot in
Image(systemName: dot <= level ? "circle.fill" : "circle")
.font(.system(size: 7))
.foregroundColor(dot <= level ? .white : .gray.opacity(0.3))
}
Text("To Try")
.font(.system(size: 9))
.foregroundColor(.secondary)
}
}
case .star(let level):
HStack(spacing: 3) {
if level == 0 {
Text("Unrated (Been)")
.font(.system(size: 10, weight: .semibold))
.foregroundColor(.secondary)
} else {
ForEach(1...5, id: \.self) { star in
Image(systemName: star <= level ? "star.fill" : "star")
.font(.system(size: 7))
.foregroundColor(star <= level ? .yellow : .gray.opacity(0.3))
}
Text("Been")
.font(.system(size: 9))
.foregroundColor(.secondary)
}
}
}
}
} | `GroupHeader` struct | Defines the `GroupHeader` struct. Conforms to View. |
| ▶ ROW | | |
| private struct FoodRow: View {
let entry: WatchEntry
var body: some View {
VStack(alignment: .leading, spacing: 2) {
Text(entry.playName)
.font(.system(size: 14, weight: .semibold))
.foregroundColor(lavender)
.lineLimit(2)
if !entry.cuisine.isEmpty {
Text(entry.cuisine)
.font(.system(size: 11))
.foregroundColor(.white.opacity(0.75))
.lineLimit(1)
}
if !entry.neighborhood.isEmpty {
Text(entry.neighborhood)
.font(.system(size: 10))
.foregroundColor(.secondary)
.lineLimit(1)
}
}
.padding(.vertical, 2)
}
} | `FoodRow` struct | Defines the `FoodRow` struct. Conforms to View. |
| #Preview {
NavigationStack { WatchFoodView() }
.environmentObject(WatchEntryStore())
} | Code block | See source code for full implementation. |