'Updating state variable does not update TextField's text
I have TextField in a form which should be updated dynamically, on a specific user action. Since I'm embedding this SwiftUI view inside a UIKit view, I had to customize it a bit:
/// Base TextField
struct CustomTextFieldSUI: View {
@State var text: String = ""
var placeholder: Text
var disableAutoCorrect = false
var isSecure: Bool = false
var onEditingChanged: (Bool) -> () = { _ in }
var onCommit: () -> () = { }
var onChange: (String) -> Void
var keyboardType: UIKeyboardType = .default
var contentType: UITextContentType?
var body: some View {
ZStack(alignment: .leading) {
if text.isEmpty {
placeholder
.foregroundColor(placeholderColor)
}
if isSecure {
SecureField("", text: $text.onChange({ newText in
onChange(newText)
}), onCommit: onCommit)
.foregroundColor(foregroundColor)
} else {
TextField("", text: $text.onChange({ newText in
onChange(newText)
}), onEditingChanged: onEditingChanged, onCommit: onCommit)
.foregroundColor(foregroundColor)
.disableAutocorrection(disableAutoCorrect)
.keyboardType(keyboardType)
.textContentType(contentType)
.autocapitalization(keyboardType == UIKeyboardType.emailAddress ? .none : .words)
}
}
}
}
Which itself is used to create another custom view:
struct TextFieldWithIconSUI: View {
@State var text: String = ""
@State var isSecure: Bool
let icon: Image
let placeholder: String
var prompt: String? = nil
let disableAutoCorrect: Bool = false
var keyboardType: UIKeyboardType = .default
var contentType: UITextContentType?
var onEditingChanged: (Bool) -> () = { _ in }
var onCommit: () -> () = { }
var onChange: (String) -> Void
var body: some View {
VStack {
ZStack {
RoundedRectangle(cornerRadius: 0)
HStack(alignment: .center, spacing: iconSpacing) {
CustomTextFieldSUI(
text: text,
placeholder: Text(placeholder),
placeholderColor: .gray,
foregroundColor: .white,
disableAutoCorrect: disableAutoCorrect,
isSecure: isSecure, onEditingChanged: onEditingChanged, onCommit: onCommit, onChange: { newText in
text = newText
onChange(newText)
},
keyboardType: keyboardType,
contentType: contentType)
icon
.scaledToFit()
.onTapGesture {
isSecure.toggle()
}
}
}
if (prompt != nil) {
VStack(alignment: .leading) {
Text(prompt!)
.padding(.leading, 10)
.padding(.top, -2)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(2)
}
}
}
}
}
Now in the client view I'm using it with a list to create a simple auto-complete list:
TextFieldWithIconSUI(text: displayCountryName, isSecure: false, icon: emptyImage, placeholder: "Country", keyboardType: .default, contentType: .countryName, onEditingChanged: { changed in
self.shouldShowCountriesList = changed
}, onChange: { text in
self.displayCountryName = text
self.shouldShowCountriesList = true
})
And somewhere in my form, I'm using the text from that TextField to show the autocorrect list. The list is shown but when I select an item it does not update the TextField's text, in debugger the state variable is updated, but the UI is not showing the new value.
if shouldShowCountriesList {
VStack(alignment: .leading) {
List {
ForEach(countriesList.filter { $0.lowercased().hasPrefix(country.object.lowercased()) }.prefix(3), id: \.self) { countryName in
Text(countryName)
.onTapGesture {
self.displayCountryName = countryName
self.shouldShowCountriesList = false
}
}
}
}
}
Solution 1:[1]
I had to use Binding variables for my custom text field:
struct CustomTextFieldSUI: View {
var text: Binding<String>
var placeholder: Text
var placeholderColor: Color
var foregroundColor: Color
var disableAutoCorrect = false
var isSecure: Bool = false
var onEditingChanged: (Bool) -> () = { _ in }
var onCommit: () -> () = { }
var onChange: (String) -> Void
var keyboardType: UIKeyboardType = .default
var contentType: UITextContentType?
var body: some View {
let bindingText = Binding(
get: {
self.text.wrappedValue
},
set: {
self.text.wrappedValue = $0
onChange($0)
})
ZStack(alignment: .leading) {
if text.wrappedValue.isEmpty {
placeholder
.foregroundColor(placeholderColor)
}
if isSecure {
SecureField("", text: bindingText, onCommit: onCommit)
.foregroundColor(foregroundColor)
} else {
TextField("", text: bindingText, onEditingChanged: onEditingChanged, onCommit: onCommit)
.foregroundColor(foregroundColor)
.disableAutocorrection(disableAutoCorrect)
.keyboardType(keyboardType)
.textContentType(contentType)
.autocapitalization(keyboardType == UIKeyboardType.emailAddress ? .none : .words)
}
}
}
}
And passed the binding variables from the host.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Maysam |
