SwiftUI Пользовательский TextField с UIViewRepresentable Проблема с ObservableObject и push-представлением - PullRequest
1 голос
/ 30 января 2020

Я создал UIViewRepresentable для переноса UITextField для SwiftUI, поэтому я могу, например, сменить первого респондента, когда пользователь нажал клавишу ввода.

Это мой UIViewRepresentable (для простоты я удалил код первого респондента)

struct CustomUIKitTextField: UIViewRepresentable {

    @Binding var text: String
    var placeholder: String

    func makeUIView(context: UIViewRepresentableContext<CustomUIKitTextField>) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        textField.placeholder = placeholder
        return textField
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomUIKitTextField>) {
        uiView.text = text
        uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
        uiView.setContentCompressionResistancePriority(.required, for: .vertical)
    }

    func makeCoordinator() -> CustomUIKitTextField.Coordinator {
        Coordinator(parent: self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: CustomUIKitTextField

        init(parent: CustomUIKitTextField) {
            self.parent = parent
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }        

    }
}

На первом экране приложения есть кнопка «Войти по электронной почте», которая толкает MailView, который отображает CustomUIKitTextField и использует свойство @Published модели представления ObservableObject в качестве текста TextField.

struct MailView: View {

    @ObservedObject var viewModel: MailSignUpViewModel

    var body: some View {
        VStack {
            CustomUIKitTextField(placeholder: self.viewModel.placeholder,
                             text: self.$viewModel.mailAddress)
                .padding(.top, 30)
                .padding(.bottom, 10)

            NavigationLink(destination: UsernameView(viewModel: UsernameSignUpViewModel())) {
                Text("Next")
            }

            Spacer()            
        }
    }
}

Все работает нормально, пока я не выберу sh другой вид, такой как MailView, скажем, например, UsernameView. Он реализован точно так же, но каким-то образом CustomUIKitTextField получает вызов updateUIView с пустой строкой, когда я заканчиваю sh.

Существует дополнительное странное поведение, например, когда я оборачиваю MailView и UsernameView в другой NavigationView, все работает нормально. Но это явно не способ исправить это, так как тогда у меня будет несколько NavigationView.

Это также работает при использовании свойства @State вместо свойства @Published внутри модели представления. Но я не хочу использовать @State, поскольку я действительно хочу, чтобы код модели оставался вне поля зрения.

Есть ли кто-нибудь, кто сталкивался с такой же проблемой или схожей?

1 Ответ

0 голосов
/ 30 января 2020

Похоже, вы используете неправильный метод делегата. textFieldDidChangeSelection даст некоторые противоречивые результаты (это то, с чем вы, похоже, имеете дело). Вместо этого я рекомендую использовать textFieldDidEndEditing, который также даст вам доступ к переданному элементу управления, но он гарантирует, что вы получаете объект, так как он уходит в отставку первым респондентом. Это важно, потому что это означает, что вы получаете объект после изменения свойств и освобождаете объект респондента.

Итак, я бы изменил ваш метод делегата следующим образом:


func textFieldDidEndEditing(_ textField: UITextField) {
    parent.text = textField.text ?? ""
}

Для получения дополнительной информации см. Эту ссылку для метода textFieldDidEndEditing: https://developer.apple.com/documentation/uikit/uitextfielddelegate/1619591-textfielddidendediting

И эту ссылку для получения информации об объекте UITextFieldDelegate: https://developer.apple.com/documentation/uikit/uitextfielddelegate

РЕДАКТИРОВАТЬ

На основании комментария, если вы хотите проверять текст каждый раз, когда он изменяется на один символ, вы должны реализовать эту функцию делегата:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

     // User pressed the delete key
     if string.isEmpty {
        // If you want to update your query string here after the delete key is pressed, you can rebuild it like we are below
        return true
     }

     //this will build the full string the user intends so we can use it to build our search
     let currentText = textField.text ?? ""
     let replacementText = (currentText as NSString).replacingCharacters(in: range, with: string)
     // You can set your parent.text here if you like, or you can fire the search function as well

     // When you're done though, return true to indicate that the textField should display the changes from the user

     return true
}
...