Je remplis un UIScrollView
vertical avec de nombreux champs UITextField
de manière dynamique lors de l'exécution. Le problème que j'ai est que le clavier masquera les champs qui se trouvent dans la zone où il apparaîtra, cela peut inclure le champ que j'édite.
J'ai essayé solution KeyboardManagement de la pomme documentation et également essayé avec des notifications sur les textFieldDidBeginEditing
et textFieldDidEndEditing
mais le problème dans les deux cas est que la notification keyboardWillShow
arrive parfois en premier, et dans ce cas au cas où il ne me fait pas savoir quel champ est celui en cours d'édition.
J'ai ce code dans une classe qui implémente le protocole UITextFieldDelegate
, chaque objet de cette classe contient un référence à l'un de ces champs et fonctionne comme son délégué
class MyClass: UIViewController { var activeTextfield: CustomTextField! // This is the variable I was talking about on the previous paragraph override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) } @objc func keyboardWillShow(_ notification: Notification) { if self.view.frame.origin.y == 0 { guard let userInfo = notification.userInfo else { return } guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardFrame = keyboardSize.cgRectValue let textFieldFrame = activeTextfield!.frame // activeTextfield sometimes is nil because this notification happens before the previous code block if textFieldFrame.origin.y + textFieldFrame.size.height > keyboardFrame.origin.y { self.view.frame.origin.y -= keyboardFrame.height } } } @objc func keyboardWillHide(_ notification: Notification) { if self.view.frame.origin.y != 0 { guard let userInfo = notification.userInfo else { return } guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return } let keyboardFrame = keyboardSize.cgRectValue self.view.frame.origin.y += keyboardFrame.height } } }
La variable activeTextfield
est un référence faible à la variable dans le UIViewController
où tout cela se produit. Dans ce contrôleur de vue, j'ai le code suivant
func textFieldDidBeginEditing(_ textField: UITextField) { self.activeTextfield = self.valueTextField } func textFieldDidEndEditing(_ textField: UITextField) { self.activeTextfield = nil }
Est-il possible de forcer les méthodes déléguées UITextField
à être appelées avant la notification clavier?
Est-ce la bonne façon de gérer ce genre de situation?
Sinon, comment dois-je la gérer?
Merci
p >
3 Réponses :
Vous devez définir la propriété de balise dans vos textFields et vous enregistrer
textFieldDidBeginEditing
et textFieldDidEndEditing
comment UITextField
a été appelé.
Tout ce dont j'ai besoin est l'objet textfield, que j'obtiens comme paramètre de ces méthodes, mais je ne sais pas comment l'envoyer au ViewController avant la notification clavier (car dans la méthode appelée pour cette notification, je décide si je dois déplacer l'écran vers le haut ou non en fonction des coordonnées de ce champ de texte)
Pourquoi utilisez-vous self.view.frame.origin.y pour l'écran? Vous ScrollView Méthodes pour ce setContentToffset ou ScrollRectTuVisible.
Pour déplacer le cadre de l'UIView principale vers le haut
Comme indiqué dans votre question:
le problème dans les deux cas est que le clavier affiche la notification vient parfois en premier, et dans ce cas, cela ne me permet pas de savoir lequel le champ est celui en cours de modification
Selon le séquence d'événements décrite dans la documentation d'Apple, textFieldShouldBeginEditing
est la première méthode déléguée appelée.
Donc, vous pouvez
textFieldShouldBeginEditing
dans le délégué pour définir votre champ de texte actif, au lieu de textFieldDidBeginEditing
(assurez-vous que vous retournez vrai
à partir de textFieldShouldBeginEditing pour permettre l'édition ) keyboardDidShowNotification
au lieu de keyboardWillShowNotification
. Cela garantira que votre UITextField
est marqué avant d'obtenir le cadre / les détails du clavier.
Vous pouvez le faire assez simplement en procédant comme suit. Ajoutez d'abord des observateurs de notification dans votre vue.
// Inside UIView Extension // Get currently active textfield func getSelectedTextField() -> UITextField? { let totalTextFields = getTextFieldsInView(view: self) for textField in totalTextFields{ if textField.isFirstResponder{ return textField } } return nil } func getTextFieldsInView(view: UIView) -> [UITextField] { var totalTextFields = [UITextField]() for subview in view.subviews as [UIView] { if let textField = subview as? UITextField { totalTextFields += [textField] } else { totalTextFields += getTextFieldsInView(view: subview) } } return totalTextFields } }
Ensuite, dans votre fonction de sélection, vous pouvez avoir quelque chose comme ça
@objc func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let currentTextField = view.getSelectedTextField() { let keyboardHeight = keyboardSize.height let textFieldFrame = currentTextField.superview?.convert(currentTextField.frame, to: nil) } } }
Et votre getSelectedTextField () ressemble à ceci
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Keyboard notification NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) }
Ce n'est pas la meilleure solution mais c'est assez simple. Une fois que la notification keyboardWillShow se déclenche, vous devez trouver le premier répondeur dans votre hiérarchie de vues actuelle. Vous pouvez le faire de la manière suivante: stackoverflow.com/questions/1823317/… stackoverflow.com/a/14135456 / 6388105