diff --git a/SOPT-Stamp-iOS/Projects/Core/Sources/Literals/StringLiterals.swift b/SOPT-Stamp-iOS/Projects/Core/Sources/Literals/StringLiterals.swift index ab022c79..639edbee 100644 --- a/SOPT-Stamp-iOS/Projects/Core/Sources/Literals/StringLiterals.swift +++ b/SOPT-Stamp-iOS/Projects/Core/Sources/Literals/StringLiterals.swift @@ -39,4 +39,15 @@ public struct I18N { public static let caption3 = "완료된 미션을 확인하며\n추억을 감상할 수 있어요" public static let start = "시작하기" } + + public struct SignIn { + public static let id = "ID" + public static let enterID = "이메일을 입력해주세요." + public static let password = "Password" + public static let enterPW = "비밀번호를 입력해주세요." + public static let checkAccount = "정보를 다시 확인해 주세요." + public static let findAccount = "계정 찾기" + public static let signIn = "로그인" + public static let signUp = "회원가입" + } } diff --git a/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift b/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift index 726807c0..2e0a4f6b 100644 --- a/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift +++ b/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift @@ -13,10 +13,10 @@ import Network public class SignInRepository { - private let networkService: SignInServiceType + private let networkService: AuthService private let cancelBag = Set() - public init(service: SignInServiceType) { + public init(service: AuthService) { self.networkService = service } } diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/ModuleFactoryInterface.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/ModuleFactoryInterface.swift index 5c4bdc76..8c538908 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/ModuleFactoryInterface.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/ModuleFactoryInterface.swift @@ -11,5 +11,5 @@ import Foundation public protocol ModuleFactoryInterface { func makeSplashVC() -> SplashVC func makeOnboardingVC() -> OnboardingVC - + func makeSignInVC() -> SignInVC } diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/OnboardingScene/VC/OnboardingVC.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/OnboardingScene/VC/OnboardingVC.swift index 3c2ed271..b430cbb9 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/OnboardingScene/VC/OnboardingVC.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/OnboardingScene/VC/OnboardingVC.swift @@ -69,7 +69,8 @@ public class OnboardingVC: UIViewController { @objc private func startButtonDidTap() { - print("start btn did tap") + let vc = self.factory.makeSignInVC() + self.navigationController?.pushViewController(vc, animated: true) } } diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SampleScene/Views/.gitkeep b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SampleScene/Views/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift index fad0ea3c..013632a3 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift @@ -8,6 +8,10 @@ import UIKit +import DSKit + +import Core + import Combine import SnapKit import Then @@ -16,16 +20,129 @@ public class SignInVC: UIViewController { // MARK: - Properties + public var factory: ModuleFactoryInterface! public var viewModel: SignInViewModel! - private var cancelBag = Set() + private var cancelBag = CancelBag() // MARK: - UI Components - + + private let logoImageView = UIImageView().then { + $0.image = DSKitAsset.Assets.logo.image + $0.contentMode = .scaleAspectFit + $0.layer.masksToBounds = true + } + + private lazy var emailTextField = CustomTextFieldView(type: .subTitle) + .setTextFieldType(.email) + .setSubTitle(I18N.SignIn.id) + .setPlaceholder(I18N.SignIn.enterID) + + private lazy var passwordTextField = CustomTextFieldView(type: .subTitle) + .setTextFieldType(.password) + .setSubTitle(I18N.SignIn.password) + .setPlaceholder(I18N.SignIn.enterPW) + + private lazy var findAccountButton = UIButton(type: .system).then { + $0.setTitle(I18N.SignIn.findAccount, for: .normal) + $0.setTitleColor(DSKitAsset.Colors.gray500.color, for: .normal) + $0.titleLabel!.setTypoStyle(.caption2) + $0.addTarget(self, action: #selector(findAccountButtonDidTap), for: .touchUpInside) + } + + private lazy var signInButton = CustomButton(title: I18N.SignIn.signIn).setEnabled(false).then { + $0.addTarget(self, action: #selector(signInButtonDidTap), for: .touchUpInside) + } + + private lazy var signUpButton = UIButton(type: .system).then { + $0.setTitle(I18N.SignIn.signUp, for: .normal) + $0.setTitleColor(DSKitAsset.Colors.gray900.color, for: .normal) + $0.titleLabel!.setTypoStyle(.caption1) + $0.addTarget(self, action: #selector(signUpButtonDidTap), for: .touchUpInside) + } + // MARK: - View Life Cycle public override func viewDidLoad() { super.viewDidLoad() self.bindViewModels() + self.setUI() + self.setLayout() + self.setTapGesture() + } + + public override func viewWillAppear(_ animated: Bool) { + self.addKeyboardObserver() + } + + deinit { + self.removeKeyboardObserver() + } + + // MARK: - @objc Function + + @objc + private func findAccountButtonDidTap() { + print("find account btn did tap") + } + + @objc + private func signInButtonDidTap() { + print("sign in btn did tap") + } + + @objc + private func signUpButtonDidTap() { + print("sign up btn did tap") + } + +} + +// MARK: - UI & Layout + +extension SignInVC { + + private func setUI() { + self.view.backgroundColor = DSKitAsset.Colors.white.color + self.findAccountButton.setUnderline() + self.signUpButton.setUnderline() + } + + private func setLayout() { + self.view.addSubviews(logoImageView, emailTextField, passwordTextField, findAccountButton, signInButton, signUpButton) + + logoImageView.snp.makeConstraints { make in + make.top.equalTo(view.safeAreaLayoutGuide).offset(100.adjustedH) + make.centerX.equalToSuperview() + make.width.equalToSuperview().multipliedBy(0.7) + } + + emailTextField.snp.makeConstraints { make in + make.top.equalTo(logoImageView.snp.bottom).offset(90.adjustedH) + make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) + } + + passwordTextField.snp.makeConstraints { make in + make.top.equalTo(emailTextField.snp.bottom).offset(12.adjustedH) + make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) + } + + findAccountButton.snp.makeConstraints { make in + make.top.equalTo(passwordTextField.snp.bottom).offset(12.adjustedH) + make.trailing.equalTo(view.safeAreaLayoutGuide).inset(22) + } + + signInButton.snp.makeConstraints { make in + make.top.equalTo(findAccountButton.snp.bottom).offset(55.adjustedH) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(56) + } + + signUpButton.snp.makeConstraints { make in + make.top.equalTo(signInButton.snp.bottom).offset(15.adjustedH) + make.centerX.equalToSuperview() + make.width.equalTo(100) + make.height.equalTo(20) + } } } @@ -34,8 +151,41 @@ public class SignInVC: UIViewController { extension SignInVC { private func bindViewModels() { - let input = SignInViewModel.Input() - let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag) +// let input = SignInViewModel.Input() +// let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag) + } + + private func setTapGesture() { + let tap = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing)) + tap.cancelsTouchesInView = false + view.addGestureRecognizer(tap) + } + + private func addKeyboardObserver() { + NotificationCenter.default.addObserver(self, selector: #selector(keyboardUp), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardDown), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + private func removeKeyboardObserver() { + NotificationCenter.default.removeObserver(self) + } + + @objc func keyboardUp(notification: NSNotification) { + if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { + let keyboardRectangle = keyboardFrame.cgRectValue +// let safeHeight = self.view.safeAreaInsets.bottom + + UIView.animate( + withDuration: 0.3, + animations: { + self.view.transform = + CGAffineTransform(translationX: 0, y: -(keyboardRectangle.height)) + } + ) + } + } + + @objc func keyboardDown() { + self.view.transform = .identity } - } diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/ViewModel/SignInViewModel.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/ViewModel/SignInViewModel.swift index e51a1e47..8a25e956 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/ViewModel/SignInViewModel.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/ViewModel/SignInViewModel.swift @@ -14,7 +14,7 @@ import Domain public class SignInViewModel: ViewModelType { private let useCase: SignInUseCase - private var cancelBag = Set() + private var cancelBag = CancelBag() // MARK: - Inputs @@ -36,7 +36,7 @@ public class SignInViewModel: ViewModelType { } extension SignInViewModel { - public func transform(from input: Input, cancelBag: Set) -> Output { + public func transform(from input: Input, cancelBag: CancelBag) -> Output { let output = Output() self.bindOutput(output: output, cancelBag: cancelBag) // input,output 상관관계 작성 @@ -44,7 +44,7 @@ extension SignInViewModel { return output } - private func bindOutput(output: Output, cancelBag: Set) { + private func bindOutput(output: Output, cancelBag: CancelBag) { } } diff --git a/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift b/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift index bd12b2b4..fcee29ac 100644 --- a/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift +++ b/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift @@ -16,6 +16,8 @@ import Data public class ModuleFactory { static let shared = ModuleFactory() private init() { } + + lazy var authService = DefaultAuthService() } extension ModuleFactory: ModuleFactoryInterface { @@ -31,4 +33,14 @@ extension ModuleFactory: ModuleFactoryInterface { return onboardingVC } + public func makeSignInVC() -> Presentation.SignInVC { + let repository = SignInRepository(service: authService) + let useCase = DefaultSignInUseCase(repository: repository) + let viewModel = SignInViewModel(useCase: useCase) + let signinVC = SignInVC() + signinVC.factory = self + signinVC.viewModel = viewModel + return signinVC + } + }