Last active
May 1, 2025 07:45
-
-
Save lanserxt/6838a55cb82258cfc865ff201062fe0b to your computer and use it in GitHub Desktop.
UIViewController with TextView and keyboard handling
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
| import UIKit | |
| import SnapKit | |
| import RSKGrowingTextView | |
| final class KeyboardInputViewController: UIViewController { | |
| var heightDidChange: ((_ fieldHeight: CGFloat, _ totalHeight: CGFloat) -> Void)? | |
| private lazy var inputTextView: RSKGrowingTextView = { | |
| let view = RSKGrowingTextView() | |
| view.delegate = self | |
| view.textColor = .black | |
| view.backgroundColor = .clear | |
| view.font = .systemFont(ofSize: 19) | |
| view.maximumNumberOfLines = 10 | |
| return view | |
| }() | |
| private var bottomConstraint: Constraint? | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| view.backgroundColor = DesignSystem.Color.floatingBackground | |
| setupInputTextView() | |
| registerForKeyboardNotifications() | |
| //Setting cursor color | |
| UITextField.appearance().tintColor = .red | |
| UITextView.appearance().tintColor = .red | |
| } | |
| private let topInset = 16.0 | |
| private func setupInputTextView() { | |
| view.addSubview(inputTextView) | |
| inputTextView.snp.makeConstraints { make in | |
| make.leading.trailing.equalToSuperview().inset(topInset) | |
| self.bottomConstraint = make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom).offset(topInset / 2.0).constraint | |
| } | |
| } | |
| override func viewWillAppear(_ animated: Bool) { | |
| super.viewWillAppear(animated) | |
| //Small delay to show the keyboard | |
| Task { | |
| try? await Task.sleep(for: .milliseconds(300)) | |
| inputTextView.placeholder = "To Search, start typing" | |
| inputTextView.placeholderColor = DesignSystem.Color.white20 | |
| inputTextView.becomeFirstResponder() | |
| } | |
| } | |
| /// Registering keyboard notifs | |
| private func registerForKeyboardNotifications() { | |
| NotificationCenter.default.addObserver(self, | |
| selector: #selector(handleKeyboardWillChangeFrame), | |
| name: UIResponder.keyboardWillChangeFrameNotification, | |
| object: nil) | |
| } | |
| //Property to store keyboard height | |
| private var keyboardHeight: CGFloat = 0.0 | |
| /// Keyboard notification handler | |
| /// - Parameter notification: notification | |
| @objc private func handleKeyboardWillChangeFrame(notification: Notification) { | |
| guard let userInfo = notification.userInfo, | |
| let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, | |
| let animationCurveRawValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else { | |
| return | |
| } | |
| if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { | |
| let keyboardHeight = keyboardSize.height | |
| self.bottomConstraint?.update(offset: -keyboardHeight + topInset) | |
| let totalHeight = inputTextView.frame.height + view.safeAreaInsets.bottom + topInset + keyboardHeight | |
| heightDidChange?(inputTextView.frame.height, totalHeight) | |
| self.keyboardHeight = keyboardHeight | |
| //Animate UI | |
| let animationOptions = UIView.AnimationOptions(rawValue: animationCurveRawValue << 16) | |
| UIView.animate(withDuration: animationDuration, delay: 0, options: animationOptions, animations: { | |
| self.view.layoutIfNeeded() | |
| }, completion: nil) | |
| } | |
| } | |
| override func viewDidLayoutSubviews() { | |
| super.viewDidLayoutSubviews() | |
| //Calculating new content height | |
| let totalHeight = inputTextView.frame.height + view.safeAreaInsets.bottom + topInset + keyboardHeight | |
| heightDidChange?(inputTextView.frame.height, totalHeight) | |
| } | |
| deinit { | |
| NotificationCenter.default.removeObserver(self) | |
| } | |
| } | |
| extension KeyboardInputViewController: RSKGrowingTextViewDelegate { | |
| func growingTextView(_ textView: RSKGrowingTextView, willChangeHeight height: CGFloat) { | |
| //Animate frame of TextView | |
| UIView.animate(withDuration: 0.2) { | |
| self.view.layoutIfNeeded() | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.