Created
November 12, 2021 15:57
-
-
Save stami/54d78be217f9335d0ba2110daaab3edf to your computer and use it in GitHub Desktop.
Reusable base view controller to extend
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use as you wish! | |
import UIKit | |
import SnapKit | |
class ScrollStackViewController: UIViewController { | |
let scrollView = UIScrollView() | |
let contentView = UIView() | |
let stackView = UIStackView() | |
private lazy var keyboardAvoiding = KeyboardAvoiding(scrollView: self.scrollView, rootView: self.view, dismissKeyboardOnTap: true) | |
/// Override to set insets for the stackview | |
var stackViewInset: UIEdgeInsets { | |
return UIEdgeInsets(top: view.statusBarHeight() + 30, left: 30, bottom: 30, right: 30) | |
} | |
var avoidKeyboard: Bool { | |
return true | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
view.addSubview(scrollView) | |
scrollView.snp.makeConstraints { make in | |
make.edges.equalToSuperview() | |
} | |
scrollView.addSubview(contentView) | |
contentView.snp.makeConstraints { make in | |
make.edges.equalToSuperview() | |
make.width.equalTo(view) | |
} | |
stackView.axis = .vertical | |
stackView.alignment = .fill | |
contentView.addSubview(stackView) | |
stackView.snp.makeConstraints { make in | |
make.top.equalToSuperview().offset(stackViewInset.top) | |
make.left.equalToSuperview().offset(stackViewInset.left) | |
make.right.equalToSuperview().offset(-stackViewInset.right) | |
make.bottom.equalToSuperview().offset(-stackViewInset.bottom) | |
} | |
} | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
if avoidKeyboard { | |
keyboardAvoiding.activate() | |
} | |
} | |
override func viewWillDisappear(_ animated: Bool) { | |
super.viewWillDisappear(animated) | |
if avoidKeyboard { | |
keyboardAvoiding.deactivate() | |
} | |
} | |
func wrapWithCenteringContainer(_ contentView: UIView) -> UIView { | |
let container = UIView() | |
container.addSubview(contentView) | |
contentView.snp.makeConstraints { make in | |
make.top.bottom.centerX.equalToSuperview() | |
make.left.greaterThanOrEqualToSuperview() | |
make.right.lessThanOrEqualToSuperview() | |
} | |
return container | |
} | |
} | |
class KeyboardAvoiding { | |
private let scrollView: UIScrollView | |
private let rootView: UIView | |
private let dismissKeyboardOnTap: Bool | |
private var originalBottomInset: CGFloat? | |
private lazy var tapGestureRecognizer: UITapGestureRecognizer = { | |
let recognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) | |
recognizer.cancelsTouchesInView = false | |
return recognizer | |
}() | |
init(scrollView: UIScrollView, rootView: UIView, dismissKeyboardOnTap: Bool) { | |
self.scrollView = scrollView | |
self.rootView = rootView | |
self.dismissKeyboardOnTap = dismissKeyboardOnTap | |
} | |
func activate() { | |
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardDidShowNotification, object: nil) | |
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) | |
if dismissKeyboardOnTap { | |
rootView.addGestureRecognizer(tapGestureRecognizer) | |
} | |
} | |
func deactivate() { | |
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidShowNotification, object: nil) | |
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) | |
rootView.removeGestureRecognizer(tapGestureRecognizer) | |
} | |
@objc | |
private func dismissKeyboard() { | |
rootView.endEditing(true) | |
} | |
@objc | |
private func keyboardWillShow(_ notification: Notification) { | |
guard let userInfo = notification.userInfo as? [String: AnyObject], | |
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey]?.cgRectValue | |
else { return } | |
let convertedFrame = rootView.convert(keyboardFrame, from: nil) | |
let keyboardInset = scrollView.frame.maxY - convertedFrame.origin.y | |
var insets = scrollView.contentInset | |
// Set only once | |
if originalBottomInset == nil { | |
originalBottomInset = insets.bottom | |
} | |
insets.bottom = (originalBottomInset ?? 0) + keyboardInset | |
scrollView.contentInset = insets | |
scrollView.scrollIndicatorInsets = insets | |
} | |
@objc | |
private func keyboardWillHide(_ notification: Notification) { | |
var insets = scrollView.contentInset | |
insets.bottom = originalBottomInset ?? 0 | |
scrollView.contentInset = insets | |
scrollView.scrollIndicatorInsets = insets | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment