I've read many similar questions here about ForEach
causing TextField
to lose focus, but all of those hinge on the ForEach
using a binding parameter. This one doesn't.
I have a List
with the first row as a TextField
, and then a ForEach
that shows rows based on matching items with the text in the TextField
. Here's the code:
struct ContentView: View {
@State private var textValue = ""
var body: some View {
List {
// Text Entry
TextField("Person:", text: $textValue)
// Suggestions
ForEach(suggestions, id: \.id) { onePerson in
Text(onePerson.id)
}
}
}
var suggestions: [PersonSuggestion] {
guard !textValue.isEmpty else { return [] }
let existingPeople = Person.allCases
.filter { $0.rawValue.localizedCaseInsensitiveContains(textValue) }
.sorted(using: KeyPathComparator(\.rawValue))
.map { PersonSuggestion.existing(person: $0) }
if textValue != existingPeople.first?.id {
return [.new(name: textValue)] + existingPeople
} else {
return existingPeople
}
}
}
And the supporting types:
enum Person: String, Hashable, CaseIterable {
case John
case Paul
case George
case Ringo
}
enum PersonSuggestion: Hashable, Identifiable {
case new(name: String)
case existing(person: Person)
var id: String {
switch self {
case .new(let name):
"+ " + name
case .existing(let person):
person.rawValue
}
}
}
When I enter the first letter into the text field, the ForEach
goes from zero rows to a non-zero number (which is expected), and the text field loses focus (not expected). When I re-focus and then type more, and the number of rows goes down, the focus is not lost any more. It only gets lost when the text goes from empty to non-empty.
If I put the TextField
and the ForEach
in separate Section
s, the problem doesn't occur. But I need all the rows to be in one section, so that's no good.
As stated at the top, all the other questions like this I've found on StackOverflow are about ForEach
's that use a binding parameter. This one doesn't, and none of the mentioned solutions have worked. What am I missing here? Can you help me find a better way to fix this? :-)
I don't think this is intentional - might be a SwiftUI bug.
This seems to be caused by giving an empty collection to
ForEach
. If you just do:then there is no problem, despite the code looking really stupid :D