/d6ae93f0c672429931
Created 19 hours ago...
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
import SwiftData
import SwiftUI
struct IdentifyView: View {
// Navigation
@Environment(IdentifyCoordinator.self) private var coordinator
@State private var selectedFamily: Family?
// Search and sort
@State private var searchText: String = ""
@State private var sortOrder: SortOrder = .alphabetical
@FocusState private var searchFieldIsFocused: Bool
// Data
@Query private var families: [Family]
// Navigation title
@State private var navigationTitleVisible: Bool = true
private var filteredFamilies: [Family] {
let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
let filtered = trimmedSearchText.isEmpty
? families
: families.filter { $0.scientificName.localizedCaseInsensitiveContains(trimmedSearchText) }
switch sortOrder {
case .alphabetical:
return filtered.sorted { $0.scientificName.localizedCaseInsensitiveCompare($1.scientificName) == .orderedAscending }
case .reverseAlphabetical:
return filtered.sorted { $0.scientificName.localizedCaseInsensitiveCompare($1.scientificName) == .orderedDescending }
}
}
init() {
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = UIColor(named: "primaryBackground")
UINavigationBar.appearance().standardAppearance = appearance
}
var body: some View {
NavigationStack(path: Binding(
get: { coordinator.navigationPath },
set: { coordinator.navigationPath = $0 }
)) {
ScrollView {
Text("Identify")
.font(.largeTitle)
.bold()
.fontDesign(.serif)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.bottom, 1)
.onScrollVisibilityChange(threshold: 0.01) { isVisible in
navigationTitleVisible = isVisible
}
HStack {
TextField("Search families", text: $searchText)
.focused($searchFieldIsFocused)
.padding(.vertical, 8)
.padding(.horizontal, 12)
.background(.secondaryBackground)
.clipShape(RoundedRectangle(cornerRadius: 8))
.foregroundStyle(.primaryForeground)
.autocorrectionDisabled()
if searchFieldIsFocused {
Button {
withAnimation {
searchText = ""
searchFieldIsFocused.toggle()
}
} label: {
Text(searchFieldIsFocused ? "Cancel" : "")
.foregroundStyle(.accent)
}
.transition(.opacity.combined(with: .move(edge: .trailing)))
.padding(.vertical, -2)
}
}
.animation(.easeInOut, value: searchFieldIsFocused)
LazyVStack(alignment: .leading) {
ForEach(filteredFamilies, id: \.self) { family in
IdentifyTileView(family: family)
.onTapGesture {
HapticsManager.shared.fireHaptic(.impact)
coordinator.pushToFamilyView(for: family)
}
}
}
}
.padding(.horizontal)
.scrollIndicators(.hidden)
.navigationDestination(for: IdentifyCoordinator.Screen.self) { screen in
switch screen {
case .identify:
EmptyView()
case .species(let family):
SpeciesListView(family: family)
}
}
.toolbar {
ToolbarItem(placement: .principal) {
if !navigationTitleVisible {
Text("Identify")
.fontDesign(.serif)
.bold()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button("A-Z") {
withAnimation {
HapticsManager.shared.fireHaptic(.impact)
sortOrder = .alphabetical
}
}
Button("Z-A") {
withAnimation {
HapticsManager.shared.fireHaptic(.impact)
sortOrder = .reverseAlphabetical
}
}
} label: {
Image(systemName: sortOrder == .alphabetical ? "text.line.first.and.arrowtriangle.forward" :"text.line.last.and.arrowtriangle.forward")
.symbolRenderingMode(.hierarchical)
.foregroundStyle(Color.accentColor)
.symbolEffect(.bounce, value: sortOrder)
} primaryAction: {
HapticsManager.shared.fireHaptic(.impact)
withAnimation {
sortOrder = sortOrder == .alphabetical ? .reverseAlphabetical : .alphabetical
}
}
}
}
.background(.primaryBackground)
.foregroundStyle(.primaryForeground)
}
}
}