I noticed that the Binding is not working as expected when we use UITextfield as subViews of a SwiftUI List with the help of UIViewRepresentable protocol.
i have implemented like a dynamic form using List View and the Custom TextField Views, when i was trying to update the value of one field from the list other than first, previously updated elements data getting removed when tapping on done button of the accessory view of textField View.
I have added the code snippets below related to Custom TextField, List Row and for ListView body
TextField View
struct TextFieldView: UIViewRepresentable {
// MARK: - Internal Properties
private let inputTextField = UITextField()
public var placeholder: String
public var keyboardType: UIKeyboardType
private let actionsHandler = InputAccessoryViewActionsHandler()
@Binding public var value: String?
func makeUIView(context: Context) -> UITextField {
inputTextField.placeholder = placeholder
inputTextField.textColor = AppTheme.primaryText.color
inputTextField.font = Font.Roboto.regular.of(size: 16)
inputTextField.keyboardType = keyboardType
inputTextField.delegate = context.coordinator
inputTextField.accessibilityIdentifier = "specValueTextField"
inputTextField.isAccessibilityElement = true
if keyboardType == .numberPad {
configureAccessoryView()
}
return inputTextField
}
func updateUIView(_ uiView: UITextField, context: Context) {
if let value = self.value {
uiView.text = value
}
}
func makeCoordinator() -> Coordinator {
Coordinator($value)
}
func configureAccessoryView() {
// Accessory View
let toolbar = UIToolbar()
toolbar.sizeToFit()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil,
action: nil)
let doneButton = UIBarButtonItem(title: "Done".localized,
style: .plain,
target: self.actionsHandler,
action: #selector(self.actionsHandler.doneButtonAction))
doneButton.tintColor = AppTheme.primary.color
doneButton.accessibilityIdentifier = "Done"
toolbar.setItems([flexibleSpace, doneButton], animated: true)
self.actionsHandler.doneButtonTapped = {
self.value = self.inputTextField.text
self.inputTextField.resignFirstResponder()
}
self.inputTextField.inputAccessoryView = toolbar
}
class InputAccessoryViewActionsHandler {
public var doneButtonTapped: (() -> Void)?
@objc func doneButtonAction() {
self.doneButtonTapped?()
}
}
class Coordinator: NSObject, UITextFieldDelegate {
var text: Binding<String?>
init(_ text: Binding<String?>) {
self.text = text
}
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
if let currentValue = textField.text as NSString? {
let proposedValue = currentValue.replacingCharacters(in: range, with: string)
self.text.wrappedValue = proposedValue
}
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
}
List Row Element view
struct SpecificationRow: View {
@Binding var specification: ClassificationSpecItem
// MARK: - Body
var body: some View {
VStack(alignment: .leading) {
TextFieldView(placeholder: "Enter value",
keyboardType: keyboardType,
value: $specification.specValue)
}.padding(EdgeInsets(top: 12, leading: 0, bottom: 5, trailing: 0))
}
}
and the list body is like below
var body: some View {
VStack {
List($viewModel.specifications) { $spec in
SpecificationRow(specification: $spec)
}
}
the viewModel was confirmed to ObservableObject and the specifications property was a @Published property
Am I missing something? Or is this just a bug in SwiftUI? Any help would be greatly appreciated! Thanks!