0
votes

Comment obtenir la taille du clavier et le CGFrame du UITextField sélectionné en même temps?

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 >


1 commentaires

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


3 Réponses :


0
votes

Vous devez définir la propriété de balise dans vos textFields et vous enregistrer textFieldDidBeginEditing et textFieldDidEndEditing comment UITextField a été appelé.


3 commentaires

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



2
votes

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

  1. implémentez 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 )
  2. utilisez keyboardDidShowNotification au lieu de keyboardWillShowNotification .

Cela garantira que votre UITextField est marqué avant d'obtenir le cadre / les détails du clavier.


0 commentaires

1
votes

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)
}


0 commentaires