web-dev-qa-db-fra.com

le clavier de l'iPhone X apparaît, offrant un espace supplémentaire

J'ai créé une interface utilisateur de discussion dans laquelle j'ai ajouté une constraint pour la tableView au bas de l'écran. Je change la valeur de la contrainte en ajoutant la hauteur du clavier, ce qui fonctionne très bien sur tous les appareils, sauf l'iPhone X.

Interface utilisateur lorsque le clavier n'est pas visible:

 enter image description here

Ce qui va bien. 

Le problème est que lorsque le clavier apparaît, un espace vide est visible entre textView et le clavier: 

 enter image description here

Dois-je essayer une approche différente pour cela ou cela peut-il être résolu en utilisant des contraintes?

18
Amit

Essayez de soustraire la hauteur de l'encart inférieur de la zone de sécurité lors du calcul de la valeur de votre contrainte.

Voici un exemple d'implémentation qui gère une notification UIKeyboardWillChangeFrame.

@objc private func keyboardWillChange(_ notification: Notification) {
    guard let userInfo = (notification as Notification).userInfo, let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
    let newHeight: CGFloat
    if #available(iOS 11.0, *) {
        newHeight = value.cgRectValue.height - view.safeAreaInsets.bottom
    } else {
        newHeight = value.cgRectValue.height
    }
    myConstraint.value = newHeight
}
30
nathangitter

Mon problème était que ma vue était dans un contrôleur de vue enfant. La conversion du CGRect a fait l'affaire.

@objc private func keyboardWillChangeFrame(notification: NSNotification) {
    guard let userInfo = notification.userInfo, let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let duration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue else {
        return
    }

    let convertedFrame = view.convert(endFrame, from: nil)
    let endFrameY = endFrame.Origin.y

    let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
    let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
    let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)

    if endFrameY >= UIScreen.main.bounds.size.height {
        inputContainerViewBottomLayoutConstraint.constant = 0.0
    }  else {
        var newHeight = view.bounds.size.height - convertedFrame.Origin.y
        if #available(iOS 11.0, *) {
            newHeight = newHeight - view.safeAreaInsets.bottom
        }
        inputContainerViewBottomLayoutConstraint.constant = newHeight
    }

    UIView.animate(withDuration: duration, delay: TimeInterval(0), options: animationCurve, animations: {
        self.view.layoutIfNeeded()
    },completion: { _ in
        self.scrollView.scrollToBottom()
    })
}
0
Tyler White

Swift 4 iPhone X Je devais modifier un peu la réponse de Nathan pour que cela fonctionne. 100% celui-ci. 

Remarque: Assurez-vous que vous avez fait glisser le contrôle depuis votre contrainte inférieure pour votre vue texte/vue depuis votre storyboard jusqu'au bas de votre zone de sécurité dans votre contrôleur de vue et que vous avez également contrôlé le déplacement et la création d'un point de vente dans votre contrôleur de vue cible Navigateur de projet. Je l'ai nommé bottomConstraint dans mon exemple. Mon champ de saisie de texte est encapsulé dans une vue (messageInputContainerView) pour permettre un alignement supplémentaire du bouton d'envoi, etc. 

 Constraint Demo

Voici le code: 

@objc private func keyboardWillChange(_ notification: Notification) {
    guard let userInfo = (notification as Notification).userInfo, let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
        return
    }

    if ((userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
        var newHeight: CGFloat
        if isKeyboardShowing {
            if #available(iOS 11.0, *) {
                newHeight = value.cgRectValue.height - view.safeAreaInsets.bottom
                bottomConstraint.constant = newHeight

            }
        }
        else {
            newHeight = value.cgRectValue.height
            bottomConstraint?.constant = view.safeAreaInsets.bottom + messageInputContainerView.bounds.height

        }
    }
}
0
elarcoiris

La valeur de clavier indiquée par UIKeyboardFrame Begin UserInfoKey est différente dans ces deux cas sous iPhone X:

  • Le clavier n'était pas visible, un champ de texte devient le premier répondant
  • Le clavier était déjà visible, un champ de texte différent devient le premier répondant.

Pour obtenir la hauteur finale du clavier du clavier (y compris les encarts de zone de sécurité), utilisez UIKeyboardFrame End UserInfoKey.

Sous iOS 11 (iPhone X en particulier), vous pouvez envisager de soustraire les incrustations inférieures de la zone de sécurité.

    NSValue *keyboardEndFrameValue = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
    if (keyboardEndFrameValue != nil) {
        CGRect keyboardSize = [keyboardEndFrameValue CGRectValue];
        _keyboardHeight = keyboardSize.size.height;
        if (@available(iOS 11.0, *)) {
            CGFloat bottomSafeAreaInset = self.view.safeAreaInsets.bottom;
            _keyboardHeight -= bottomSafeAreaInset;
        } else {
            // Fallback on earlier versions
        }
    }
0
Raunak

Faites un exutoire à votre contrainte à votre ViewController. Je vais parler de yourConstraint pour le moment. Ajoutez ensuite du code pour déterminer quand le clavier est affiché et quand il est supprimé. Là, vous changez la constant de la contrainte en conséquence. Cela vous permet de continuer à utiliser des contraintes.

Dans viewWillAppear:

NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillShow), name:        NSNotification.Name.UIKeyboardWillShow, object: nil) // <=== Replace YourViewController with name of your view controller
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil) // <=== Replace YourViewController with name of your view controller

Dans viewWillDisappear:

NotificationCenter.default.removeObserver(self)

Dans votre UIViewController

@objc private func keyboardWillShow(notification: Notification) {
    guard let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
        yourConstraint.isActive = false // <=== 
        view.layoutIfNeeded()
        return
    }
    let duration: TimeInterval = ((notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue) ?? 0.4
    yourConstraint.constant = newValue // <===
    UIView.animate(withDuration: duration) {
        self.view.layoutIfNeeded()
    }
}

@objc private func keyboardWillHide(notification: Notification) {
    let duration: TimeInterval = ((notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue) ?? 0.4
    yourConstraint.constant = oldValue
    UIView.animate(withDuration: duration) {
        self.view.layoutIfNeeded()
    }
}

Le UIView.animate n'est pas nécessaire (l'intérieur du bloc l'est), mais la transition semble agréable.

0
erikmartens