Я создаю SwiftUI
приложение с использованием MVVM.
Мне нужны некоторые дополнительные поведения для текстового поля, поэтому я обертываю 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
}
}