← Back to index

WatchFoodView

Spots Watch App
CodeWhat It DoesHow It Does It
▶ IMPORTS
import SwiftUIFramework importsImports 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` structDefines 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` structDefines 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` structDefines 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` structDefines the `FoodRow` struct. Conforms to View.
#Preview { NavigationStack { WatchFoodView() } .environmentObject(WatchEntryStore()) }Code blockSee source code for full implementation.