import UIKit import Shared import Combine import InputField import ScrollViewController public final class NicknameController: UIViewController { lazy private var screenView = NicknameView() private let prefilled: String private let completion: StringClosure private let viewModel = NicknameViewModel() private var cancellables = Set<AnyCancellable>() private let keyboardListener = KeyboardFrameChangeListener(notificationCenter: .default) public init(_ prefilled: String, _ completion: @escaping StringClosure) { self.prefilled = prefilled self.completion = completion super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { nil } public override func loadView() { let view = UIView() view.addSubview(screenView) screenView.snp.makeConstraints { $0.top.equalToSuperview() $0.left.equalToSuperview() $0.right.equalToSuperview() $0.bottom.equalToSuperview().offset(0) } self.view = view } public override func viewDidLoad() { super.viewDidLoad() setupKeyboard() setupBindings() screenView.inputField.update(content: prefilled) viewModel.didInput(prefilled) } private func setupKeyboard() { keyboardListener.keyboardFrameWillChange = { [weak self] keyboard in guard let self = self else { return } let inset = self.view.frame.height - self.view.convert(keyboard.frame, from: nil).minY self.screenView.snp.updateConstraints { $0.bottom.equalToSuperview().offset(-inset) } self.view.setNeedsLayout() UIView.animate(withDuration: keyboard.animationDuration) { self.view.layoutIfNeeded() } } } private func setupBindings() { viewModel.state .map(\.status) .receive(on: DispatchQueue.main) .sink { [weak screenView] in screenView?.update(status: $0) } .store(in: &cancellables) viewModel.done .receive(on: DispatchQueue.main) .sink { [unowned self] in dismiss(animated: true) completion($0) }.store(in: &cancellables) screenView.inputField.textPublisher .sink { [weak viewModel] in viewModel?.didInput($0) } .store(in: &cancellables) screenView.saveButton.publisher(for: .touchUpInside) .sink { [weak viewModel] in viewModel?.didTapSave() } .store(in: &cancellables) } }