使用自定义 UITextField(通过使用 UIViewRepresentable)作为子视图时列表视图的奇怪行为



我注意到,当我们在UIViewRepresentable协议的帮助下使用UITextfield作为SwiftUI列表的子视图时,绑定没有按预期工作。

当我尝试从列表中更新除第一个字段之外的一个字段的值时,我已经使用列表视图和自定义文本字段视图实现了像动态表单一样,以前更新的元素数据在点击 TextField 视图附件视图的完成按钮时被删除。

我在下面添加了与自定义文本字段,列表行和列表视图正文相关的代码片段

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)
}
}

视图模型已确认为可观察对象,规范属性是@Published属性

我错过了什么吗?或者这只是 SwiftUI 中的一个错误?任何帮助将不胜感激!谢谢!

您在错误的位置创建了视图对象。而不是:

private let inputTextField = UITextField()

做:

private let inputTextField: UITextField?
func makeUIView(context: Context) -> UITextField {
inputTextField = UITextField()

或者也许你不需要坚持参考?

此外,您不能在 SwiftUI 中创建这样的对象:

private let actionsHandler = InputAccessoryViewActionsHandler()

您可以改用协调器对象来处理操作。

SwiftUI 视图结构应仅初始化值类型。如果实例化对象,则需要将其包装在@StateObject中,该将生存期与出现/消失的View相关联,或者如果与应用程序的生存期相关联,则可以是全局或单例。

同样在updateUIView中,您需要从属性更新文本字段属性。

您正在updateUIView方法中更新文本字段值,而不是makeUIViewTextFieldView类。

只需将您的代码替换为以下代码即可

func makeUIView(context: Context) -> UITextField {
//Assign your value to UITextField here
inputTextField.text = self.value
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) {
//Comment below code

/* if let value = self.value {
uiView.text = value
}*/
}

最新更新