SwiftUI 自定义 TextField 与 UIViewRepresentable 与 ObservableObject 和推送视图的问题

2023-11-21

我创建了一个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()            
        }
    }
}

一切正常,直到我推送另一个视图(例如 MailView),例如用户名查看。它的实现方式完全相同,但不知何故CustomUIKitTextField得到一个updateUIView当我完成输入后,用空字符串调用。

还有其他奇怪的行为,例如当我将 MailView 和 UsernameView 包装在另一个 NavigationView 中时,一切正常。但这显然不是解决它的方法,因为那时我会有多个导航视图。

当在视图模型中使用 @State 属性而不是 @Published 属性时,它也适用。但我不想使用@State,因为我真的想将模型代码保留在视图之外。

有没有人遇到过同样的问题或类似的问题?


您似乎使用了错误的委托方法。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

EDIT

根据评论,如果您希望在每次更改一个字符时检查文本,则应该实现此委托函数:

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
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SwiftUI 自定义 TextField 与 UIViewRepresentable 与 ObservableObject 和推送视图的问题 的相关文章

随机推荐