我正在SwiftUI
使用 MVVM 构建一个应用程序。
我需要一些额外的文本字段行为,所以我将 a 包装UITextField
在一个UIViewRepresentable
视图中。
@State
如果我在包含我的文本字段的视图中使用简单的文本来绑定文本,则自定义文本字段的行为符合预期;但由于我想将我的文本字段的所有文本存储在视图模型中,我使用的是@ObservedObject
; 使用它时,文本字段绑定不起作用:它看起来总是重置为初始状态(空文本)并且它不发布任何值(并且视图不刷新)。
这种奇怪的行为只发生在UIViewRepresentable
视图中。
我的主视图包含一个表单,它看起来像这样:
struct LoginSceneView: View {
@ObservedObject private var viewModel: LoginViewModel = LoginViewModel()
var body: some View {
ScrollView(showsIndicators: false) {
VStack(spacing: 22) {
UIKitTextField(text: $viewModel.email, isFirstResponder: $viewModel.isFirstResponder)
SecureField("Password", text: $viewModel.password)
Button(action: {}) {
Text("LOGIN")
}
.disabled(!viewModel.isButtonEnabled)
}
.padding(.vertical, 40)
}
}
}
视图模型是这样的:
class LoginViewModel: ObservableObject {
@Published var email = ""
@Published var password = ""
@Published var isFirstResponder = false
var isButtonEnabled: Bool { !email.isEmpty && !password.isEmpty }
}
最后,这是我的自定义文本字段:
struct UIKitTextField: UIViewRepresentable {
// MARK: - Coordinator
class Coordinator: NSObject, UITextFieldDelegate {
private let textField: UIKitTextField
fileprivate init(_ textField: UIKitTextField) {
self.textField = textField
super.init()
}
@objc fileprivate func editingChanged(_ sender: UITextField) {
let text = sender.text ?? ""
textField.text = text
textField.onEditingChanged(text)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.textField.onEditingBegin()
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.textField.onEditingEnd()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.textField.onReturnKeyPressed()
}
}
// MARK: - Properties
@Binding private var text: String
private let onEditingChanged: (String) -> Void
private let onEditingBegin: () -> Void
private let onEditingEnd: () -> Void
private let onReturnKeyPressed: () -> Bool
// MARK: - Initializers
init(text: Binding<String>,
onEditingChanged: @escaping (String) -> Void = { _ in },
onEditingBegin: @escaping () -> Void = {},
onEditingEnd: @escaping () -> Void = {},
onReturnKeyPressed: @escaping () -> Bool = { true }) {
_text = text
self.onEditingChanged = onEditingChanged
self.onEditingBegin = onEditingBegin
self.onEditingEnd = onEditingEnd
self.onReturnKeyPressed = onReturnKeyPressed
}
// MARK: - UIViewRepresentable methods
func makeCoordinator() -> Coordinator { Coordinator(self) }
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
textField.setContentHuggingPriority(.defaultHigh, for: .vertical)
textField.addTarget(context.coordinator, action: #selector(Coordinator.editingChanged(_:)), for: .editingChanged)
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text
}
}