Skip to content

Instantly share code, notes, and snippets.

@jeremiegirault
Last active March 13, 2017 13:23
Show Gist options
  • Save jeremiegirault/cbe59ddb33550e94eb8dd645b0f5c86d to your computer and use it in GitHub Desktop.
Save jeremiegirault/cbe59ddb33550e94eb8dd645b0f5c86d to your computer and use it in GitHub Desktop.
Handle iOS keyboard with autolayout
private final class KeyboardObserver {
private static var key: UInt8 = 0
let guide = UILayoutGuide()
private var isAttached = false
private let top: NSLayoutConstraint
private let left: NSLayoutConstraint
private let width: NSLayoutConstraint
private let height: NSLayoutConstraint
static func layoutGuide(for viewController: UIViewController) -> UILayoutGuide {
if let observer = objc_getAssociatedObject(viewController, &KeyboardObserver.key) as? KeyboardObserver { return observer.guide }
let observer = KeyboardObserver(in: viewController.view)
objc_setAssociatedObject(viewController, &KeyboardObserver.key, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return observer.guide
}
private init(in view: UIView) {
view.addLayoutGuide(guide)
top = guide.topAnchor.constraint(equalTo: view.topAnchor)
left = guide.leadingAnchor.constraint(equalTo: view.leadingAnchor)
width = guide.widthAnchor.constraint(equalToConstant: 0)
height = guide.heightAnchor.constraint(equalToConstant: 0)
// default constraints to bottom
NSLayoutConstraint.activate([
guide.widthAnchor.constraint(equalTo: view.widthAnchor).priority(UILayoutPriorityDefaultLow),
guide.heightAnchor.constraint(equalToConstant: 0).priority(UILayoutPriorityDefaultLow),
guide.leadingAnchor.constraint(equalTo: view.leadingAnchor).priority(UILayoutPriorityDefaultLow),
guide.topAnchor.constraint(equalTo: view.bottomAnchor).priority(UILayoutPriorityDefaultLow)
])
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillChange(notification:)),
name: .UIKeyboardWillChangeFrame,
object: nil)
}
@objc private func keyboardWillChange(notification: NSNotification) {
guard let view = guide.owningView else { return }
let beginFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let endFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
let animationCurve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
let fromFrameInView = view.convert(beginFrame, from: nil)
let toFrameInView = view.convert(endFrame, from: nil)
update(fromFrameInView)
UIView.animate(withDuration: animationDuration,
delay: 0,
options: UIViewAnimationOptions(rawValue: animationCurve),
animations: {
self.update(toFrameInView)
}, completion: nil)
}
private func update(_ rect: CGRect) {
top.constant = rect.origin.y
left.constant = rect.origin.x
width.constant = rect.size.width
height.constant = rect.size.height
if !isAttached {
isAttached = true
NSLayoutConstraint.activate([ top, left, width, height ])
}
guide.owningView?.layoutIfNeeded()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment