Hello, I'm trying to figure out how to wireup following UI
Here is the picker, aka my attempt to have a custom one for items that are stored remotely with search and refresh features:
import SwiftUI
import Combine
struct CustomPicker: View {
@State var loading = false
@State var item: CustomPickerItem?
@State var items: [CustomPickerItem] = []
@StateObject private var search = DebouncedState(initialValue: "")
var body: some View {
List(selection: $item) {
ForEach(items) { item in
Text(item.name).tag(item)
}
}
.overlay {
if loading {
ContentUnavailableView("Loading", systemImage: "arrow.down.circle.dotted", description: Text("Retrieving items list"))
}
else if items.isEmpty {
ContentUnavailableView("Empty", systemImage: "doc.text.magnifyingglass", description: Text("Empty list retrieved"))
}
}
.task { await load() }
.refreshable { await load() }
.searchable(text: $search.currentValue)
.onChange(of: search.debouncedValue, { oldValue, newValue in
Task { await load() }
})
.onChange(of: item, { oldValue, newValue in
guard let name = newValue?.name else { return }
print("Selected \(name)")
})
}
func load() async {
loading = true
item = nil
// pretend we are retrieving list from backend
let sleepSeconds = 2
try? await Task.sleep(nanoseconds: UInt64(sleepSeconds) * NSEC_PER_SEC)
var items = (1...20).map { num in
let name = "Item \(num)"
return CustomPickerItem(id: "\(num)", name: name)
}
if search.debouncedValue != "" {
items = items.filter { $0.name.localizedStandardContains(search.debouncedValue) }
}
self.items = items
loading = false
}
}
private class DebouncedState<Value>: ObservableObject {
@Published var currentValue: Value
@Published var debouncedValue: Value
init(initialValue: Value, delay: Double = 0.3) {
_currentValue = Published(initialValue: initialValue)
_debouncedValue = Published(initialValue: initialValue)
$currentValue
.debounce(for: .seconds(delay), scheduler: RunLoop.main)
.assign(to: &$debouncedValue)
}
}
struct CustomPickerItem: Codable, Identifiable, Hashable {
let id: String
let name: String
}
#Preview {
NavigationStack {
CustomPicker()
}
}
And the question is how to wire it up to the form view? aka:
import SwiftUI
struct CustomForm: View {
@State private var number = 1
@State private var numbers = [1,2,3]
@State private var item: CustomPickerItem?
var body: some View {
Form {
Section("Default") {
Picker("Number", selection: $number) {
ForEach(numbers, id: \.self) { number in
Text("\(number)")
}
}
.pickerStyle(.navigationLink)
}
Section("Custom") {
NavigationLink {
CustomPicker()
.onSubmit {
print("Submitted?")
}
} label: {
HStack {
Text("Custom")
Spacer()
Text(item?.name ?? "N/A").foregroundStyle(.secondary)
}
}
}
}
.navigationTitle("Pickers")
}
}
#Preview {
NavigationStack {
CustomForm()
}
}
Wondering if someone can point me the direction how to handle list selection in parent view and closing picker, or may be this one is bad approach in general and should be done differently