diff --git a/VDSSample/ViewControllers/BaseViewController.swift b/VDSSample/ViewControllers/BaseViewController.swift index d8fc4c1..2702453 100644 --- a/VDSSample/ViewControllers/BaseViewController.swift +++ b/VDSSample/ViewControllers/BaseViewController.swift @@ -22,7 +22,7 @@ public class FormSection: UIStackView { } } - private var titleLabel = Label().with { $0.isHidden = true; $0.textStyle = .boldBodyLarge } + private var titleLabel = Label().with { $0.isHidden = true; $0.textStyle = .boldBodyLarge; } public override init(frame: CGRect) { super.init(frame: frame) @@ -164,10 +164,8 @@ public class BaseViewController: UIViewController, Initable { super.viewDidLoad() view.backgroundColor = .white - let top = UIView.makeWrapper(for: contentTopView, edgeSpacing: 16, isTrailing: false) - // Add the top and bottom views to the stack view - stackView.addArrangedSubview(top) + stackView.addArrangedSubview(contentTopView.makeWrapper(edgeSpacing: 16)) stackView.addArrangedSubview(bottomScrollView) // Add the stack view to the view controller's view @@ -178,7 +176,7 @@ public class BaseViewController: UIViewController, Initable { stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -25) ]) bottomScrollView.addSubview(contentBottomView) @@ -191,7 +189,7 @@ public class BaseViewController: UIViewController, Initable { contentBottomView.bottomAnchor.constraint(equalTo: bottomScrollView.bottomAnchor), contentBottomView.widthAnchor.constraint(equalTo: bottomScrollView.widthAnchor) ]) - + contentBottomView.addSubview(formStackView) formStackView.pinToSuperView(.init(top: 0, left: 16, bottom: 0, right: edgeSpacing)) @@ -202,6 +200,52 @@ public class BaseViewController: UIViewController, Initable { picker.isHidden = true setupForm() + + NotificationCenter.default + .publisher(for: UIResponder.keyboardWillShowNotification) + .sink { [weak self] notification in + self?.keyboardWillShow(notification: notification) + }.store(in: &subscribers) + + NotificationCenter.default + .publisher(for: UIResponder.keyboardWillHideNotification) + .sink { [weak self] notification in + self?.keyboardWillHide(notification: notification) + }.store(in: &subscribers) + + NotificationCenter.default + .publisher(for: UITextField.textDidBeginEditingNotification) + .sink { [weak self] notification in + guard let self, let textField = notification.object as? UITextField else { return } + self.activeTextField = textField + }.store(in: &subscribers) + + NotificationCenter.default + .publisher(for: UITextField.textDidEndEditingNotification) + .sink { [weak self] notification in + self?.activeTextField?.resignFirstResponder() + self?.activeTextField = nil + }.store(in: &subscribers) + } + + func isViewHiddenByKeyboard(view: UIView, keyboardFrame: CGRect) -> Bool { + let viewFrameInWindow = view.convert(view.bounds, to: nil) + let inetersectionFrame = viewFrameInWindow.intersection(keyboardFrame) + return inetersectionFrame.height > 0 + } + + func keyboardWillShow(notification: UIKit.Notification) { + if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { + if let activeTextField, self.view.frame.origin.y == 0, isViewHiddenByKeyboard(view: activeTextField, keyboardFrame: keyboardSize) { + self.view.frame.origin.y -= keyboardSize.height + } + } + } + + func keyboardWillHide(notification: UIKit.Notification) { + if self.view.frame.origin.y != 0 { + self.view.frame.origin.y = 0 + } } public func setupForm() { @@ -236,31 +280,25 @@ public class BaseViewController: UIViewController, Initable { return formStackView.addFormRow(label: label, view: view) } + var activeTextField: UITextField? + open func setup() { if let textFields = allTextFields() { for textField in textFields { - if textField.isNumeric { - let keypadToolbar: UIToolbar = UIToolbar() - - // add a done button to the numberpad - keypadToolbar.items=[ - UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil), - UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: textField, action: #selector(UITextField.resignFirstResponder)) - ] - keypadToolbar.sizeToFit() - - // add a toolbar with a done button above the number pad - textField.inputAccessoryView = keypadToolbar - textField.keyboardType = .numberPad - } + let keypadToolbar: UIToolbar = UIToolbar() + + // add a done button to the numberpad + keypadToolbar.items=[ + UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil), + UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: textField, action: #selector(UITextField.resignFirstResponder)) + ] + keypadToolbar.sizeToFit() + + // add a toolbar with a done button above the number pad + textField.inputAccessoryView = keypadToolbar + textField.keyboardType = textField.isNumeric ? .numberPad : .alphabet textField.returnKeyType = .done - textField - .publisher(for: .editingDidEndOnExit) - .sink { textField in - textField.resignFirstResponder() - } - .store(in: &subscribers) } } } @@ -270,4 +308,5 @@ public class BaseViewController: UIViewController, Initable { } open func allTextFields() -> [TextField]? { nil } + }