1、问题背景
在研究使用IQKeyBoardManager来处理键盘的弹出和放下等一些列事件的时候,页面使用的是UITableView,并在cell中绘制了多个文本输入框。由于项目是基于UINavigationController建立的,当键盘弹出时发现navigationBar 被自动隐藏了。等到键盘放下后navigationBar却没有像期望的那样重新回到当初的位置。第一感觉是不是使用了IQKeyBoardManager,而IQKeyBoardManager在处理键盘遮挡的时候更改了navigationbar的位置,照着这个想法随便研究了一下IQKeyBoardManager。
2、IQKeyBoardManager 设计思想及加载过程
它继承自NSObject,设计的方案是作为一个单例来加载,并且实现了NSObject 的类方法 load 来完成初始化,由于他采用的机制是将Manager作为键盘、设备方向相关事件的观察者,并且基于响应者当前界面的响应者链完成了相关控件的管理。
3、问题的最终确诊及解决方案
各种尝试更改IQKeyBoardManager在处理键盘弹出等事件相关方法都无果后,做了个移除IQKeyBoardManager的测试。结果发现navigationbar依然会被隐藏,经过一阵眩晕后发现原来NavigationController的在IOS8之后新增了如下属性,闲时还是得多读书呀……
/// When the keyboard appears, the navigation controller's navigationBar toolbar will be hidden. The bars will remain hidden when the keyboard dismisses, but a tap in the content area will show them.
@available(iOS 8.0, *)
open var hidesBarsWhenKeyboardAppears: Bool
/// When the user swipes, the navigation controller's navigationBar & toolbar will be hidden (on a swipe up) or shown (on a swipe down). The toolbar only participates if it has items.
@available(iOS 8.0, *)
open var hidesBarsOnSwipe: Bool
/// When the UINavigationController's vertical size class is compact, hide the UINavigationBar and UIToolbar. Unhandled taps in the regions that would normally be occupied by these bars will reveal the bars.
@available(iOS 8.0, *)
open var hidesBarsWhenVerticallyCompact: Bool
/// When the user taps, the navigation controller's navigationBar & toolbar will be hidden or shown, depending on the hidden state of the navigationBar. The toolbar will only be shown if it has items to display.
@available(iOS 8.0, *)
open var hidesBarsOnTap: Bool
看第一个属性的注释,内心有一种放完炮就走的感觉,还好他留下了能解决问题的人,于是将希望寄托在了hidesBarsOnTap属性上。
单纯的通过在keyboard show和hidden时设置hidesBarsOnTap的true和false并没有完美的解决我的问题。随后增加了navigationbar显示和隐藏的监听。
最终解决方案的代码如下:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
NotificationCenter.default.addObserver(self, selector: #selector(keyboardHidden(notify:)), name:NSNotification.Name.UIKeyboardDidHide , object:nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShow(notify:)), name:NSNotification.Name.UIKeyboardDidShow , object:nil)
navigationController?.navigationBar.addObserver(self, forKeyPath: "hidden", options: NSKeyValueObservingOptions.new, context: nil)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.hidesBarsWhenKeyboardAppears = false
navigationController?.hidesBarsOnSwipe = false
navigationController?.hidesBarsWhenVerticallyCompact = false
navigationController?.hidesBarsOnTap = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidHide, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
navigationController?.navigationBar.removeObserver(self, forKeyPath: "hidden", context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "hidden",change != nil {
let flag:Bool = change![NSKeyValueChangeKey.newKey] as! Bool
if flag {
self.navigationController?.hidesBarsOnTap = true
}else{
self.navigationController?.hidesBarsOnTap = false
}
}
}
@objc func keyboardHidden(notify:Notification){
self.navigationController?.hidesBarsOnTap = navigationController?.isNavigationBarHidden == true ? true:false
}
@objc func keyboardShow(notify:Notification){
self.navigationController?.hidesBarsOnTap = true
}
目前的解决方案还不是很完美,欢迎各位留言指正。