Reputation: 790
In the following code, which you can copy into a new SwiftUI project, when I run it and start typing into the textfield, the field loses focus after each character. How can I fix this? I dont understand about observable objects so perhaps this is the problem? and BTW, I'm using Xcode 13.0 Beta 5
struct CmdOption: Hashable, Identifiable {
var id: UUID { UUID() }
var value: String
}
import SwiftUI
struct ContentView: View {
@State var theOptions: [CmdOption] = [
CmdOption(value: ""),
CmdOption(value: "")
]
@State var allOptions: [CmdOption] = []
var body: some View {
VStack {
ScrollView(.vertical) {
ForEach($allOptions, id: \.self) { $option in
VStack {
TextField(option.value, text: $option.value)
}
}
}
}.onAppear {
startup()
}
}
func startup() {
allOptions.removeAll()
theOptions.forEach {option in
allOptions.append(option)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Upvotes: 2
Views: 539
Reputation: 30411
There are two problems:
id
as a computed property. This means that is will change every time you try read the value.id: \.self
, which is not unique (since id
was also stored as a computed property, it isn't considered for default Hashable
conformance).To fix this, change CmdOption
to the following:
struct CmdOption: Hashable, Identifiable {
let id: UUID = UUID()
var value: String
}
And your ForEach
(implicitly id: \.id
):
ForEach($allOptions) { $option in
VStack {
TextField(option.value, text: $option.value)
}
}
Now, these view's IDs are constant and unique. The TextField
s will now no longer lose focus, because they aren't recreating the view every time ContentView
's body is recomputed, as their ID has remained the same. See Demystify SwiftUI - WWDC21 to learn more about how IDs affect a view's lifecycle.
Upvotes: 1