问题
I am facing an strange behavior with InputAccessoryView, I am working on chat screen where I have used InputAccessoryView
where I have registered for KeyboardWillShow
and KeyboardWillHide
notifications. When my chat screen appears it automatically calls the KeyboardWillShowMethod
once and after that it hides automatically without calling the KeyboardWillHide
notification. After Loading chats when I click on textbox to type text it calls KeyboardWillShow
which is fine. But when I try to hide keybaord it calls two methods first it will call KeyboardWillHide
and after that it will call KeyboardWillShow
which is strange.
This is my chat screen Image when keyboard is hidden. This is when keyboard is shown Image
I am using this InputAccessoryView
Code Programatically inputAccessoryView
This is how I have registered for keyboard notifications.
func handleKeyBoard(){
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
var contentInset = self.collectionView?.contentInset
contentInset?.bottom = keyboardSize.maxY
self.collectionView?.contentInset = contentInset!
self.collectionView?.scrollIndicatorInsets = contentInset!
// collectionViewBottomAnchor?.constant = keyboardSize.height + 50
// print ("input height \(inputAccessoryView?.frame.maxY) ")
// print("keyboard height \(keyboardSize.height)")
// print("keyboard Y \(keyboardSize.maxY)")
// print("keyboard Y \(keyboardSize.minY)")
//print("keyboard Y \(inputAccessoryView.framemaxY)")
if self.messages.count > 0{
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (completed:Bool) in
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
})
}
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
print("keyboard hide")
self.collectionView?.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 52, right: 0)
self.collectionView?.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 52, right: 0)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (completed:Bool) in
})
}
}
In selectors I am trying to change my CollectionView
Insets according to a Y Index of Keyboard because I am not getting Height of keybaord that is also an issue. Height of kyeboard is always 50 as of height of inputAccessoryView.
回答1:
Here is the solution which I have found Thanx to @Amit. Instead of using UIKeyboardFrameBeginUserInfoKey
I have used UIKeyboardFrameEndUserInfoKey
after doing this I was able to get accurate hight of keyboard in KeyboardWillAppear
method. Now the problem which remains was that KeyboardWillShow
method was get called after KeyboardWillHide
but at that time the KeyboardWillShow
have a hight of keyboard 50.
That means when i try to hide a keyboard it will call KeyboardWillHide which is fine and after that It automatically calls KeyboardWillShow
but height of keyboard remains 50 so I put condition there.
Now following method will make effect only when height is more than 50.
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if keyboardSize.height > 50{
var contentInset = self.collectionView?.contentInset
contentInset?.bottom = keyboardSize.height + 50
self.collectionView?.contentInset = contentInset!
self.collectionView?.scrollIndicatorInsets = contentInset!
if self.messages.count > 0{
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (completed:Bool) in
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
})
}
}
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
self.collectionView?.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 52, right: 0)
self.collectionView?.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 52, right: 0)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (completed:Bool) in
})
}
}
回答2:
When keyboard has input accessory view, keyboardDidHideNotification
is observed twice:
- When keyboard is hidden.
- When input accessroy view is hidden (note that input accessory view is visible for a while after keyboard is dismissed).
If your implementation depends on selector to be called just once, you can do one of following workarounds:
Option A: Check keyboard frame:
@objc
private func keyboardDidHide(_ notification: Notification) {
guard let keyboardRect = notification.keyboardRect, keyboardRect.origin.y == view.frame.maxY else {
return
}
// Do whatever you need...
}
extension Notification {
var keyboardRect: CGRect? {
guard let keyboardSize = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return nil
}
return keyboardSize.cgRectValue
}
}
Option B: Throttle reaction using GDC:
private var pendingKeyboardDidHideRequestWorkItem: DispatchWorkItem?
private func keyboardDidHide(_ notification: Notification) {
pendingKeyboardDidHideRequestWorkItem?.cancel()
let keyboardDidHideRequestWorkItem = DispatchWorkItem { [weak self] in
// Do whatever you need...
}
pendingKeyboardDidHideRequestWorkItem = keyboardDidHideRequestWorkItem
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500), execute: keyboardDidHideRequestWorkItem)
}
来源:https://stackoverflow.com/questions/51355483/input-accessory-view-behave-unexpectedly-with-keyboard-hide-and-show-events