[英]SwiftUI Custom TextField with UIViewRepresentable Issue with ObservableObject and pushed View
我創建了一個UIViewRepresentable
來為 SwiftUI 包裝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 的CustomUIKitTextField
,並使用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,例如 UsernameView。 它以完全相同的方式實現,但不知何故,一旦我完成輸入, CustomUIKitTextField
就會以空字符串獲取updateUIView
調用。
還有一些奇怪的行為,例如當我將 MailView 和 UsernameView 包裝在另一個 NavigationView 中時,一切正常。 但這顯然不是解決問題的方法,因為那時我會有多個 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
編輯
根據評論,如果您希望在每次更改一個字符時檢查文本,您應該實現這個委托函數:
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
}
我還需要 SwiftUI 中的UITextField
表示,它會對每個字符的變化做出反應,所以我選擇了binaryPilot84的答案。 雖然UITextFieldDelegate
方法textField(_:shouldChangeCharactersIn:replacementString:)
很棒,但它有一個警告——每次我們用這個方法更新文本時,光標都會移動到文本的末尾。 這可能是期望的行為。 但是,它不適合我,所以我像這樣實現了目標操作:
public func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.addTarget(context.coordinator, action: #selector(context.coordinator.textChanged), for: .editingChanged)
return textField
}
public final class Coordinator: NSObject {
@Binding private var text: String
public init(text: Binding<String>) {
self._text = text
}
@objc func textChanged(_ sender: UITextField) {
guard let text = sender.text else { return }
self.text = text
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.