// TODO: 开篇
- 常见问题(本文要解决的)
- 思路(用protocol个extension)
1、在Notification的extension中方便快捷地获取键盘的动画时长和尺寸
extension Notification {
public var keyboardSize: CGSize? {
return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size
}
public var keyboardAnimationDuration: TimeInterval? {
return userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval
}
}
2、定义协议,协议中需要暴露两方面的接口,一是开启和结束键盘状态的监听,二是监听到键盘升降之后的action
public protocol KeyboardObserving: AnyObject {
func startObservingKeyboard()
func stopObservingKeyboard()
func keyboardWillShow(notification: Notification)
func keyboardWillHide(notification: Notification)
}
UIViewController有生命周期,需要在特定的阶段开启或取消对通知的监听,在willAppear
和willDisappear
阶段调用func startObservingKeyboard()
及func stopObservingKeyboard()
是比较方便的。尤其是present出另一个viewController的情况下,如果没有取消对键盘的监听,那么第二个ViewController触发键盘可能会影响第一个控制器执行升起键盘后的动画。
这个协议希望被需要监听键盘即将升降的对象来遵守,通常是UIViewController
,所以我们在默认实现中也通过where Self: UIViewController
限制为UIViewController
。
public extension KeyboardObserving where Self: UIViewController {
func startObservingKeyboard() {
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { [weak self] (notification) in
self?.keyboardWillShow(notification: notification)
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil) { [weak self] (notification) in
self?.keyboardWillHide(notification: notification)
}
}
func stopObservingKeyboard() {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
}
3、用例
class MediaBrowseViewController: UIViewController, KeyboardObserving {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 在生命周期的即将显示阶段 调用协议中的开启监听方法的默认实现
self.startObservingKeyboard()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 在生命周期的即将消失阶段 调用协议中的结束监听方法的默认实现
self.stopObservingKeyboard()
}
// MARK: - KeyboardObserving
func keyboardWillShow(notification: Notification) {
self.replyInputView.showMaskIfPossible()
if let height = notification.keyboardSize?.height, let duration = notification.keyboardAnimationDuration {
// 这里执行键盘即将升起时UI变化的任务
UIView.animate(withDuration: duration) {
self.replyInputView.willAscend()
self.replyInputView.snp.updateConstraints { make in
make.bottom.equalToSuperview().offset(-height)
}
self.view.layoutSubviews()
}
}
}
func keyboardWillHide(notification: Notification) {
if let duration = notification.keyboardAnimationDuration {
// 这里执行键盘即将降下时UI变化的任务
UIView.animate(withDuration: duration) {
self.replyInputView.willDescend()
self.replyInputView.snp.updateConstraints { make in
make.bottom.equalToSuperview().offset(-tabbarHeight)
}
self.view.layoutIfNeeded()
}
}
}
}
4、关键词:
- UIViewController的
生命周期
protocol
extension