diff --git a/Smashing-Assignment/Extension/UITextField+.swift b/Smashing-Assignment/Extension/UITextField+.swift index b8372b1..74c36ca 100644 --- a/Smashing-Assignment/Extension/UITextField+.swift +++ b/Smashing-Assignment/Extension/UITextField+.swift @@ -1,12 +1,12 @@ // // UITextField+.swift -// NewCombine +// Smashing-Assignment // -// Created by JIN on 12/26/25. +// Created by 홍준범 on 12/26/25. // -import Combine import UIKit +import Combine extension UITextField { func textDidChangePublisher() -> AnyPublisher { @@ -17,4 +17,22 @@ extension UITextField { } } - +extension UITextField { + func addLeftPadding(_ width: CGFloat = 10) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: self.frame.height)) + self.leftView = paddingView + self.leftViewMode = ViewMode.always + } + + func addRightPadding(_ width: CGFloat = 10) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: self.frame.height)) + self.rightView = paddingView + self.rightViewMode = ViewMode.always + } + + /// 텍스트 필드에 좌우 패딩을 한 번에 추가합니다. + func addPadding(leftAmount: CGFloat = 10, rightAmount: CGFloat = 10) { + addLeftPadding(leftAmount) + addRightPadding(rightAmount) + } +} diff --git a/Smashing-Assignment/Presentation/Core/CombineViewController_HJB.swift b/Smashing-Assignment/Presentation/Core/CombineViewController_HJB.swift deleted file mode 100644 index 76ed8dd..0000000 --- a/Smashing-Assignment/Presentation/Core/CombineViewController_HJB.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// Combine_HJB.swift -// Smashing-Assignment -// -// Created by 홍준범 on 12/26/25. -// - -import UIKit -import Combine - -import Then -import SnapKit - - -class CombineViewController_HJB: UIViewController { - @Published var combineText: String = "" - - private var cancellables = Set() - - private let homeView = CombineView_HJB() - - override func loadView() { - self.view = homeView - } - - override func viewDidLoad() { - super.viewDidLoad() - - bind() - addTarget() - } - - private func bind() { - homeView.combineTextField.textDidChangePublisher() - .assign(to: &$combineText) - - $combineText - .sink { [weak self] text in - if text.count >= 5 { - self?.homeView.combineButton.isEnabled = true - self?.homeView.combineButton.backgroundColor = .systemBlue - self?.homeView.infoText.isHidden = true - - } else { - self?.homeView.combineButton.isEnabled = false - self?.homeView.combineButton.backgroundColor = .gray - self?.homeView.infoText.isHidden = false - } - } - .store(in: &cancellables) - } - - private func addTarget() { - homeView.combineButton.addTarget(self, action: #selector(combineButtonTapped), for: .touchUpInside) - } - - @objc - private func combineButtonTapped() { - print("Tapped") - } -} diff --git a/Smashing-Assignment/Presentation/Core/TabBarController.swift b/Smashing-Assignment/Presentation/Core/TabBarController.swift index 9b8dfe3..3a09f7f 100644 --- a/Smashing-Assignment/Presentation/Core/TabBarController.swift +++ b/Smashing-Assignment/Presentation/Core/TabBarController.swift @@ -39,7 +39,8 @@ final class TabBarController: UITabBarController { case .jinjae: return JinJaeViewController() case .junbeom: - return CombineViewController_HJB() + let HJBviewModel = CombineViewModel_HJB() + return CombineViewController_HJB(viewModel: HJBviewModel) case .seungjun: return UIViewController() } diff --git a/Smashing-Assignment/Presentation/Core/UITextField+.swift b/Smashing-Assignment/Presentation/Core/UITextField+.swift deleted file mode 100644 index 74c36ca..0000000 --- a/Smashing-Assignment/Presentation/Core/UITextField+.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// UITextField+.swift -// Smashing-Assignment -// -// Created by 홍준범 on 12/26/25. -// - -import UIKit -import Combine - -extension UITextField { - func textDidChangePublisher() -> AnyPublisher { - NotificationCenter.default - .publisher(for: UITextField.textDidChangeNotification, object: self) - .map { _ in self.text ?? "" } - .eraseToAnyPublisher() - } -} - -extension UITextField { - func addLeftPadding(_ width: CGFloat = 10) { - let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: self.frame.height)) - self.leftView = paddingView - self.leftViewMode = ViewMode.always - } - - func addRightPadding(_ width: CGFloat = 10) { - let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: self.frame.height)) - self.rightView = paddingView - self.rightViewMode = ViewMode.always - } - - /// 텍스트 필드에 좌우 패딩을 한 번에 추가합니다. - func addPadding(leftAmount: CGFloat = 10, rightAmount: CGFloat = 10) { - addLeftPadding(leftAmount) - addRightPadding(rightAmount) - } -} diff --git a/Smashing-Assignment/Presentation/HJB/CombineViewController_HJB.swift b/Smashing-Assignment/Presentation/HJB/CombineViewController_HJB.swift new file mode 100644 index 0000000..606fdb7 --- /dev/null +++ b/Smashing-Assignment/Presentation/HJB/CombineViewController_HJB.swift @@ -0,0 +1,153 @@ +// +// Combine_HJB.swift +// Smashing-Assignment +// +// Created by 홍준범 on 12/26/25. +// + +import UIKit +import Combine + +import Then +import SnapKit + +protocol InputOutputProtocol { + + associatedtype Input + associatedtype Output + + func transform(input: AnyPublisher) -> AnyPublisher +} + +class CombineViewModel_HJB: InputOutputProtocol { + + enum Input { + case firstTextFieldChanged(String) + case secondTextFieldChanged(String) + case buttonTapped + } + + enum Output { + case toggleButton(isEnabled: Bool) + case clearTextFields + } + + private let output: PassthroughSubject = .init() + private var cancellables = Set() + + private let firstTextSubject = CurrentValueSubject("") + private let secondTextSubject = CurrentValueSubject("") + + func transform(input: AnyPublisher) -> AnyPublisher { + input.sink { [weak self] event in + switch event { + case .firstTextFieldChanged(let text): + self?.firstTextSubject.send(text) + case .secondTextFieldChanged(let text): + self?.secondTextSubject.send(text) + case .buttonTapped: + self?.firstTextSubject.send("") + self?.secondTextSubject.send("") + self?.output.send(.clearTextFields) + } + }.store(in: &cancellables) + + Publishers.CombineLatest(firstTextSubject, secondTextSubject) + .map { first, second in + return first.count >= 5 && second.count >= 5 + } + .sink { [weak self] isEnabled in + self?.output.send(.toggleButton(isEnabled: isEnabled)) + }.store(in: &cancellables) + + return output.eraseToAnyPublisher() + } +} + +class CombineViewController_HJB: UIViewController { + @Published var combineText: String = "" + +// private let vm = CombineViewModel_HJB() + private let vm: CombineViewModel_HJB + + private let input: PassthroughSubject = .init() + private var cancellables = Set() + + private let homeView = CombineView_HJB() + + init(viewModel: CombineViewModel_HJB) { + self.vm = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + self.view = homeView + } + + override func viewDidLoad() { + super.viewDidLoad() + + bind() + addTarget() + } + + private func bind() { + homeView.combineTextField.textDidChangePublisher() + .sink { [weak self] text in + self?.input.send(.firstTextFieldChanged(text)) + }.store(in: &cancellables) + + homeView.secondCombineTextField.textDidChangePublisher() + .sink { [weak self] text in + self?.input.send(.secondTextFieldChanged(text)) + }.store(in: &cancellables) + + let output = vm.transform(input: input.eraseToAnyPublisher()) + + output + .receive(on: DispatchQueue.main) //이거 없애서 해보기 + .sink { [weak self] event in + switch event { + case .toggleButton(let isEnabled): + self?.homeView.combineButton.isEnabled = isEnabled + self?.homeView.combineButton.backgroundColor = isEnabled ? .systemBlue : .gray + case .clearTextFields: + self?.homeView.combineTextField.text = "" + self?.homeView.secondCombineTextField.text = "" + } + }.store(in: &cancellables) + } + + private func addTarget() { + homeView.combineButton.addTarget(self, action: #selector(combineButtonTapped), for: .touchUpInside) + } + + @objc + private func combineButtonTapped(_ sender: UIButton) { + input.send(.buttonTapped) + } +} + +// private func bind() { +// homeView.combineTextField.textDidChangePublisher() +// .assign(to: &$combineText) +// +// $combineText +// .sink { [weak self] text in +// if text.count >= 5 { +// self?.homeView.combineButton.isEnabled = true +// self?.homeView.combineButton.backgroundColor = .systemBlue +// self?.homeView.infoText.isHidden = true +// +// } else { +// self?.homeView.combineButton.isEnabled = false +// self?.homeView.combineButton.backgroundColor = .gray +// self?.homeView.infoText.isHidden = false +// } +// } +// .store(in: &cancellables) +// } diff --git a/Smashing-Assignment/Presentation/Core/CombineView_HJB.swift b/Smashing-Assignment/Presentation/HJB/CombineView_HJB.swift similarity index 71% rename from Smashing-Assignment/Presentation/Core/CombineView_HJB.swift rename to Smashing-Assignment/Presentation/HJB/CombineView_HJB.swift index 3e89579..b47fa82 100644 --- a/Smashing-Assignment/Presentation/Core/CombineView_HJB.swift +++ b/Smashing-Assignment/Presentation/HJB/CombineView_HJB.swift @@ -18,8 +18,15 @@ final class CombineView_HJB: UIView { $0.addPadding() } + var secondCombineTextField = UITextField().then { + $0.placeholder = "Combine2" + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.gray.cgColor + $0.addPadding() + } + var infoText = UILabel().then { - $0.text = "5글자 이상 입력해주세요" + $0.text = "두개의 텍스트 박스에 모두 5글자 이상 입력 시 삭제 가능" $0.textAlignment = .center } @@ -27,6 +34,7 @@ final class CombineView_HJB: UIView { $0.setTitle("Next", for: .normal) $0.setTitleColor(.black, for: .normal) $0.layer.cornerRadius = 8 + $0.backgroundColor = .gray } override init(frame: CGRect) { @@ -41,6 +49,7 @@ final class CombineView_HJB: UIView { private func setUI() { addSubview(combineTextField) + addSubview(secondCombineTextField) addSubview(infoText) addSubview(combineButton) } @@ -53,9 +62,16 @@ final class CombineView_HJB: UIView { $0.leading.trailing.equalToSuperview().inset(40) } - infoText.snp.makeConstraints { + secondCombineTextField.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(combineTextField.snp.bottom).offset(20) + $0.height.equalTo(50) + $0.leading.trailing.equalToSuperview().inset(40) + } + + infoText.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalTo(secondCombineTextField.snp.bottom).offset(20) } combineButton.snp.makeConstraints {