带有 UIViewRepresentable 的 SwiftUI 自定义文本字段 ObservableObject 和推送视图的问题
Posted
技术标签:
【中文标题】带有 UIViewRepresentable 的 SwiftUI 自定义文本字段 ObservableObject 和推送视图的问题【英文标题】:SwiftUI Custom TextField with UIViewRepresentable Issue with ObservableObject and pushed View 【发布时间】:2020-01-30 16:42:19 【问题描述】:我为 SwiftUI 创建了一个 UIViewRepresentable
来包装 UITextField
,所以我可以例如当用户点击回车键时更改第一响应者。
这是我的 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 ?? ""
应用程序的第一个屏幕有一个“使用电子邮件登录”按钮,该按钮会推送显示 CustomUIKitTextField
的 MailView,并使用 ObservableObject
视图模型的 @Published 属性作为 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 中时,一切正常。但这显然不是解决问题的方法,因为那时我会有多个 NavigationView。
当在视图模型中使用 @State 属性而不是 @Published 属性时,它也可以工作。但我不想使用@State,因为我真的想将模型代码保留在视图之外。
有没有人遇到过同样的问题或类似的问题?
【问题讨论】:
【参考方案1】:您似乎使用了错误的委托方法。 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
【讨论】:
感谢您的帮助,但我认为这不会有帮助,因为我的视图模型不会收到用户输入的每个字符的通知。当然,在某些情况下这可能就足够了,但如果我想触发搜索功能的请求,这个解决方案就不够了。 @sinalco12,我用适当的委托函数更新了我的答案,以便在每个字符发生变化时检查文本。以上是关于带有 UIViewRepresentable 的 SwiftUI 自定义文本字段 ObservableObject 和推送视图的问题的主要内容,如果未能解决你的问题,请参考以下文章
将 UIViewRepresentable 连接到 SwiftUI
SwiftUI:避免使用 MKMapView+UIViewRepresentable 在 TabView 中重新创建/重新渲染视图