Skip to content

Instantly share code, notes, and snippets.

@daehn
Last active April 29, 2019 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daehn/09ece006aa90f359802619a59dd606c8 to your computer and use it in GitHub Desktop.
Save daehn/09ece006aa90f359802619a59dd606c8 to your computer and use it in GitHub Desktop.
import UIKit
/// Object observing keyboard changes and passing a `KeyboardChangeInfo` to notify about changes.
final class KeyboardObserver: NSObject {
typealias KeyboardChangeClosure = (KeyboardChangeInfo) -> Void
let changeClosure: KeyboardChangeClosure
// MARK: - Private
private var showToken: NSObjectProtocol?
private var hideToken: NSObjectProtocol?
private let notificationCenter = NotificationCenter.default
// MARK: - Lifecycle
init(closure: @escaping KeyboardChangeClosure) {
changeClosure = closure
super.init()
setupObservers()
}
deinit {
if let showToken = showToken {
notificationCenter.removeObserver(showToken)
}
if let hideToken = hideToken {
notificationCenter.removeObserver(hideToken)
}
}
// MARK: - Observation
private func setupObservers() {
// swiftlint:disable trailing_closure
showToken = notificationCenter.addObserver(
forName: UIApplication.keyboardWillShowNotification,
object: nil,
queue: .main,
using: { [weak self] in self?.processNotification($0, .show) }
)
hideToken = notificationCenter.addObserver(
forName: UIApplication.keyboardWillHideNotification,
object: nil,
queue: .main,
using: { [weak self] in self?.processNotification($0, .hide) }
)
// swiftlint:enable trailing_closure
}
private func processNotification(_ note: Notification, _ type: KeyboardChangeInfo.ChangeType) {
guard let info = KeyboardChangeInfo(note: note, type: type) else { return }
changeClosure(info)
}
}
/// Value holding keyboard change relevant animation.
struct KeyboardChangeInfo {
enum ChangeType {
case show, hide
}
let frameEnd: CGRect
let frameBegin: CGRect
let duration: TimeInterval
let animationOptions: UIView.AnimationOptions
let changeType: ChangeType
// MARK: - Lifecycle
init?(note: Notification, type: ChangeType) {
let userInfo = note.userInfo
guard let frameEnd = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
let frameBegin = userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect,
let duration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let curveValue = userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else { return nil }
changeType = type
self.frameEnd = frameEnd
self.frameBegin = frameBegin
self.duration = duration
self.animationOptions = UIView.AnimationOptions(rawValue: curveValue)
}
}
// MARK: - Helper
extension KeyboardChangeInfo {
func animateAlongside(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
UIView.animate(using: self, animations: animations, completion: completion)
}
}
extension UIView {
static func animate(
using changeInfo: KeyboardChangeInfo,
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil
) {
animate(
withDuration: changeInfo.duration,
delay: 0,
options: changeInfo.animationOptions,
animations: animations,
completion: completion
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment