// // AutocompleteTextField.swift // WorkoutsPlus // // Created by Felix Förtsch on 19.09.24. // import SwiftUI struct AutocompleteTextField: View { var placeholder: String @Binding var item: Item? var items: [Item] @State private var searchText = "" var filteredItems: [Item] { if searchText.isEmpty { return [] } else { return items.filter { $0.name.localizedCaseInsensitiveContains(searchText) } } } @State private var isAutoCompleteShown: Bool = true var body: some View { Group { HStack { TextField(placeholder, text: $searchText) // TODO: Fix "List line" not extending to the full width if item == nil && !searchText.isEmpty { Text("NEW") .font(.caption) .foregroundStyle(.green) } if item != nil { Text("EDIT") .font(.caption) .foregroundStyle(.orange) } } .onAppear() { if item != nil { self.searchText = item!.name } } .onChange(of: searchText) { if filteredItems.count == 1 && filteredItems.first?.name == searchText { self.item = filteredItems.first isAutoCompleteShown = false } else { self.item = nil isAutoCompleteShown = true } } if isAutoCompleteShown { ForEach(filteredItems, id: \.self) { item in HStack { Text(item.name) .foregroundStyle(.gray) Spacer() Image(systemName: "arrow.up.left") .foregroundStyle(.blue) } // This .contentShape makes the whole row tappable .contentShape(Rectangle()) .onTapGesture { self.item = item searchText = item.name isAutoCompleteShown = false } } } } } } private struct Item: Nameable { var id = UUID() var name: String } #Preview { @Previewable @State var item: Item? = nil VStack { Text("Preview only: ") Text(item?.name ?? "No item selected") } .background(.red) List { AutocompleteTextField(placeholder: "New Item", item: $item, items: [ Item(name: "Item 1"), Item(name: "Item 2"), Item(name: "Item 3") ]) } }