Skip to content

Instantly share code, notes, and snippets.

@danielt1263
Last active December 4, 2018 21:38
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 danielt1263/508e965dfa0dcbc21825 to your computer and use it in GitHub Desktop.
Save danielt1263/508e965dfa0dcbc21825 to your computer and use it in GitHub Desktop.
//
// KeyboardScrollController.swift
//
// Created by Daniel Tartaglia on 9/16/15.
// Copyright © 2015. MIT License.
//
import UIKit
final class KeyboardScrollController: NSObject {
/// `view` is the primary view of the screen
/// `bottomConstraint` is the constraint that connects the bottom of the scroll view to the bottom of the main view.
init(view: UIView, bottomConstraint: NSLayoutConstraint) {
self.view = view
self.scrollView = view.findDeepChildSatisfying { $0 is UIScrollView } as! UIScrollView
self.bottomConstraint = bottomConstraint
savedConstant = bottomConstraint.constant
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrameNotification), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardWillShowNotification(notification: Notification) {
guard let keyboardInfo = KeyboardInfo(notification) else { return }
view.layoutIfNeeded()
UIView.animate(withDuration: keyboardInfo.animationDuration, animations: {
[unowned self] in
self.bottomConstraint.constant = max(keyboardInfo.frameEnd.height, self.savedConstant)
self.view.layoutIfNeeded()
})
if let firstResponder = self.view.findDeepChildSatisfying(criteria: { $0.isFirstResponder }), firstResponder is UITextView {
let frame = self.scrollView.convert(firstResponder.bounds, from: firstResponder)
self.scrollView.scrollRectToVisible(frame, animated: false)
}
}
@objc func keyboardWillHideNotification(notification: Notification) {
guard let keyboardInfo = KeyboardInfo(notification) else { return }
view.layoutIfNeeded()
UIView.animate(withDuration: keyboardInfo.animationDuration, animations: {
[unowned self] in
self.bottomConstraint.constant = self.savedConstant
self.view.layoutIfNeeded()
})
}
@objc func keyboardWillChangeFrameNotification(notification: Notification) {
guard let keyboardInfo = KeyboardInfo(notification) else { return }
view.layoutIfNeeded()
UIView.animate(withDuration: keyboardInfo.animationDuration, animations: {
[unowned self] in
self.bottomConstraint.constant = max(keyboardInfo.frameEnd.height, self.savedConstant)
self.view.layoutIfNeeded()
})
}
private let view: UIView
private let scrollView: UIScrollView
private let bottomConstraint: NSLayoutConstraint
private let savedConstant: CGFloat
}
struct KeyboardInfo {
var animationCurve: UIView.AnimationCurve
var animationDuration: Double
var isLocal: Bool
var frameBegin: CGRect
var frameEnd: CGRect
}
extension KeyboardInfo {
init?(_ notification: Notification) {
guard notification.name == UIResponder.keyboardWillShowNotification || notification.name == UIResponder.keyboardWillChangeFrameNotification else { return nil }
let u = notification.userInfo!
animationCurve = UIView.AnimationCurve(rawValue: u[UIWindow.keyboardAnimationCurveUserInfoKey] as! Int)!
animationDuration = u[UIWindow.keyboardAnimationDurationUserInfoKey] as! Double
isLocal = u[UIWindow.keyboardIsLocalUserInfoKey] as! Bool
frameBegin = u[UIWindow.keyboardFrameBeginUserInfoKey] as! CGRect
frameEnd = u[UIWindow.keyboardFrameEndUserInfoKey] as! CGRect
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment