Keyboard height change observer
If you search on UIKeyboardWillShowNotification
in the Xcode docs you get to the section on UIWindow, which has a table of notifications at the end.
I suggest trying the UIKeyboardWillChangeFrameNotification
.
Time to find the answer: About 30 seconds.
How to get height of Keyboard?
Swift
You can get the keyboard height by subscribing to the UIKeyboardWillShowNotification
notification. (Assuming you want to know what the height will be before it's shown).
Swift 4
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
@objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
}
}
Swift 3
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil
)
@objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
}
}
Swift 2
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
func keyboardWillShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo!
let keyboardFrame: NSValue = userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let keyboardRectangle = keyboardFrame.CGRectValue()
let keyboardHeight = keyboardRectangle.height
}
UITableView adds height of keyboard to contentSize when the keyboard appears on iOS11
Workaround
This is not specifically answering the original question, but might be a solution for those who don't have a translucent keyboard and input view.
I could get around this problem by changing the constraints and not setting the bottom insets. Initially the bottom constraint of the table view was set to the bottom of the super view (bottom of the screen, basically). So when the keyboard appeared, I didn't change the table view's frame, but the bottom inset. This apparently didn't work properly.
Now I've set the bottom constraint of the table view to the top of the input view (black bar) and the bottom inset to zero. Since the input view moves up when the keyboard appears, it changes the frame of the table view and the bottom inset remains zero. I still set the content offset, because I need specific behavior in different situations, but that's it.
This only works in my situation, because I neither have a translucent input bar nor keyboard and don't need to show blurry content behind it.
Detect keyboard height while UIScrollView is scrolled down and the keypad is being interactively dragged
As of iOS 10, Apple doesn't provide a NSNotification
observer to detect the frame change while the keypad is dragged interactively by UIScrollView
, UIKeyboardWillChangeFrame
and UIKeyboardDidChangeFrame
are observed only once releasing tap.
Anyways, after looking around DAKeyboardControl library, I had the idea to attach UIScrollView.UIPanGestureRecognizer
in the UIViewController
, so any gesture events that are produced will be handled in UIViewController
as well. After screwing around several hours, I got it to work, here is all the code that is necessary for this:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
fileprivate let collectionView = UICollectionView(frame: .zero)
private let bottomView = UIView()
fileprivate var bottomInset: NSLayoutConstraint!
// This holds height of keypad
private var maxKeypadHeight: CGFloat = 0 {
didSet {
self.updateCollectionViewInsets(maxKeypadHeight + self.bottomView.frame.height)
self.bottomInset.constant = -maxKeypadHeight
}
}
private var isListeningKeypadChange = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keypadWillChange(_:)), name: .UIKeyboardWillChangeFrame, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keypadWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keypadWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keypadDidHide), name: .UIKeyboardDidHide, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
func keypadWillShow(_ notification: Notification) {
guard !self.isListeningKeypadChange, let userInfo = notification.userInfo as? [String : Any],
let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval,
let animationCurve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue
else {
return
}
self.maxKeypadHeight = value.cgRectValue.height
let options = UIViewAnimationOptions.beginFromCurrentState.union(UIViewAnimationOptions(rawValue: animationCurve))
UIView.animate(withDuration: animationDuration, delay: 0, options: options, animations: { [weak self] in
self?.view.layoutIfNeeded()
}, completion: { finished in
guard finished else { return }
// Some delay of about 500MS, before ready to listen other keypad events
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
self?.beginListeningKeypadChange()
}
})
}
func handlePanGestureRecognizer(_ pan: UIPanGestureRecognizer) {
guard self.isListeningKeypadChange, let windowHeight = self.view.window?.frame.height else { return }
let barHeight = self.bottomView.frame.height
let keypadHeight = abs(self.bottomInset.constant)
let usedHeight = keypadHeight + barHeight
let dragY = windowHeight - pan.location(in: self.view.window).y
let newValue = min(dragY < usedHeight ? max(dragY, 0) : dragY, self.maxKeypadHeight)
print("Old: \(keypadHeight) New: \(newValue) Drag: \(dragY) Used: \(usedHeight)")
guard keypadHeight != newValue else { return }
self.updateCollectionViewInsets(newValue + barHeight)
self.bottomInset.constant = -newValue
}
func keypadWillChange(_ notification: Notification) {
if self.isListeningKeypadChange, let value = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
self.maxKeypadHeight = value.cgRectValue.height
}
}
func keypadWillHide(_ notification: Notification) {
guard let userInfo = notification.userInfo as? [String : Any] else { return }
self.maxKeypadHeight = 0
var options = UIViewAnimationOptions.beginFromCurrentState
if let animationCurve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt {
options = options.union(UIViewAnimationOptions(rawValue: animationCurve))
}
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval
UIView.animate(withDuration: duration ?? 0, delay: 0, options: options, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
func keypadDidHide() {
self.collectionView.panGestureRecognizer.removeTarget(self, action: nil)
self.isListeningKeypadChange = false
if (self.maxKeypadHeight != 0 || self.bottomInset.constant != 0) {
self.maxKeypadHeight = 0
}
}
private func beginListeningKeypadChange() {
self.isListeningKeypadChange = true
self.collectionView.panGestureRecognizer.addTarget(self, action: #selector(self.handlePanGestureRecognizer(_:)))
}
fileprivate func updateCollectionViewInsets(_ value: CGFloat) {
let insets = UIEdgeInsets(top: 0, left: 0, bottom: value + 8, right: 0)
self.collectionView.contentInset = insets
self.collectionView.scrollIndicatorInsets = insets
}
}
Related Topics
Bridgetoobjectivec Not Available on Swift Beta 5
How to Get Wkwebview to Work in Swift and for an Macos App
How to Distinguish Bool and Int in Swift
In Swift 5, What Is a Way to Compare Pointers to Two Closures
Migrate from Racsignal to Reactiveswift or Rac5
Swift 3 Google Map Add Markers on Touch
Xcode Server: Opening Import File for Module 'Mobilecoreservices': Permission Denied
How to Observe Object's Property in Rxswift
Dictionary of a Protocol Swift 4
Detecting End of The Playback in Avaudioplayer
Xcode 9.2 Is Not Showing Swift 4.1
How to Convert Custom Object to Data Swift
How to Make Nsbutton Title at Bottom and Centered
Parametrized Unit Tests in Swift
Error "[Sharesheet] Connection Invalidated" Error iOS13+ But Not on iOS 11.4