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:
Ce qui va bien.
Le problème est que lorsque le clavier apparaît, un espace vide est visible entre textView et le clavier:
Dois-je essayer une approche différente pour cela ou cela peut-il être résolu en utilisant des contraintes?
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
}
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()
})
}
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.
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
}
}
}
La valeur de clavier indiquée par UIKeyboardFrame Begin UserInfoKey est différente dans ces deux cas sous iPhone X:
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
}
}
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.