Skip to content

Commit

Permalink
feat/#157 정규식에 따라 텍스트필드 상태 업데이트하는 ViewModel 바인딩
Browse files Browse the repository at this point in the history
  • Loading branch information
youz2me committed Jul 12, 2024
1 parent 6844c3e commit af28c01
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 41 deletions.
20 changes: 16 additions & 4 deletions KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,15 @@
DD3072242C3C0EB200416D9F /* MyPromiseReadyInfoRequestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3072232C3C0EB200416D9F /* MyPromiseReadyInfoRequestModel.swift */; };
DD3072262C3C0F0B00416D9F /* PromiseLateInfoResponseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3072252C3C0F0B00416D9F /* PromiseLateInfoResponseModel.swift */; };
DD3072282C3C104D00416D9F /* ArrivalCompletionResponseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3072272C3C104D00416D9F /* ArrivalCompletionResponseModel.swift */; };
DD4393762C412F4500EC1799 /* EnterInviteCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD43936D2C412F4500EC1799 /* EnterInviteCodeView.swift */; };
DD4393762C412F4500EC1799 /* InviteCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD43936D2C412F4500EC1799 /* InviteCodeView.swift */; };
DD4393772C412F4500EC1799 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD43936E2C412F4500EC1799 /* CreateGroupView.swift */; };
DD4393782C412F4500EC1799 /* CheckInviteCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD43936F2C412F4500EC1799 /* CheckInviteCodeView.swift */; };
DD4393792C412F4500EC1799 /* JoinButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4393702C412F4500EC1799 /* JoinButtonView.swift */; };
DD43937A2C412F4500EC1799 /* FinishCreateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4393722C412F4500EC1799 /* FinishCreateViewController.swift */; };
DD43937B2C412F4500EC1799 /* CreateGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4393732C412F4500EC1799 /* CreateGroupViewController.swift */; };
DD43937C2C412F4500EC1799 /* EnterInviteCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4393742C412F4500EC1799 /* EnterInviteCodeViewController.swift */; };
DD43937D2C412F4500EC1799 /* CheckInviteCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4393752C412F4500EC1799 /* CheckInviteCodeViewController.swift */; };
DD43937F2C41357800EC1799 /* InviteCodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD43937E2C41357800EC1799 /* InviteCodeViewModel.swift */; };
DD931B6B2C3D9EBB00526452 /* ReadyStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD931B6A2C3D9EBB00526452 /* ReadyStatusView.swift */; };
DD931B6E2C3DA27F00526452 /* ParticipantCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD931B6D2C3DA27F00526452 /* ParticipantCollectionViewCell.swift */; };
DD931B722C3DA92700526452 /* EnterReadyInfoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD931B712C3DA92700526452 /* EnterReadyInfoButtonView.swift */; };
Expand Down Expand Up @@ -194,14 +195,15 @@
DD3072232C3C0EB200416D9F /* MyPromiseReadyInfoRequestModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPromiseReadyInfoRequestModel.swift; sourceTree = "<group>"; };
DD3072252C3C0F0B00416D9F /* PromiseLateInfoResponseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromiseLateInfoResponseModel.swift; sourceTree = "<group>"; };
DD3072272C3C104D00416D9F /* ArrivalCompletionResponseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrivalCompletionResponseModel.swift; sourceTree = "<group>"; };
DD43936D2C412F4500EC1799 /* EnterInviteCodeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterInviteCodeView.swift; sourceTree = "<group>"; };
DD43936D2C412F4500EC1799 /* InviteCodeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InviteCodeView.swift; sourceTree = "<group>"; };
DD43936E2C412F4500EC1799 /* CreateGroupView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = "<group>"; };
DD43936F2C412F4500EC1799 /* CheckInviteCodeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckInviteCodeView.swift; sourceTree = "<group>"; };
DD4393702C412F4500EC1799 /* JoinButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinButtonView.swift; sourceTree = "<group>"; };
DD4393722C412F4500EC1799 /* FinishCreateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FinishCreateViewController.swift; sourceTree = "<group>"; };
DD4393732C412F4500EC1799 /* CreateGroupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateGroupViewController.swift; sourceTree = "<group>"; };
DD4393742C412F4500EC1799 /* EnterInviteCodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterInviteCodeViewController.swift; sourceTree = "<group>"; };
DD4393752C412F4500EC1799 /* CheckInviteCodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckInviteCodeViewController.swift; sourceTree = "<group>"; };
DD43937E2C41357800EC1799 /* InviteCodeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteCodeViewModel.swift; sourceTree = "<group>"; };
DD931B6A2C3D9EBB00526452 /* ReadyStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadyStatusView.swift; sourceTree = "<group>"; };
DD931B6D2C3DA27F00526452 /* ParticipantCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantCollectionViewCell.swift; sourceTree = "<group>"; };
DD931B712C3DA92700526452 /* EnterReadyInfoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterReadyInfoButtonView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -479,14 +481,15 @@
children = (
DD43936C2C412F4500EC1799 /* View */,
DD4393712C412F4500EC1799 /* ViewController */,
DD4393802C41357D00EC1799 /* ViewModel */,
);
path = GroupCreate;
sourceTree = "<group>";
};
DD43936C2C412F4500EC1799 /* View */ = {
isa = PBXGroup;
children = (
DD43936D2C412F4500EC1799 /* EnterInviteCodeView.swift */,
DD43936D2C412F4500EC1799 /* InviteCodeView.swift */,
DD43936E2C412F4500EC1799 /* CreateGroupView.swift */,
DD43936F2C412F4500EC1799 /* CheckInviteCodeView.swift */,
DD4393702C412F4500EC1799 /* JoinButtonView.swift */,
Expand All @@ -505,6 +508,14 @@
path = ViewController;
sourceTree = "<group>";
};
DD4393802C41357D00EC1799 /* ViewModel */ = {
isa = PBXGroup;
children = (
DD43937E2C41357800EC1799 /* InviteCodeViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
DD865B652C3920F600C351A2 /* Onboarding */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1057,6 +1068,7 @@
DD931B722C3DA92700526452 /* EnterReadyInfoButtonView.swift in Sources */,
A3FB18512C3BF531001483E5 /* RegisterMeetingsResponseModel.swift in Sources */,
789AD4B32C3C0093002E2688 /* SocialLoginResponseModel.swift in Sources */,
DD43937F2C41357800EC1799 /* InviteCodeViewModel.swift in Sources */,
DE159D352C406E1600425101 /* MyPageNavigationView.swift in Sources */,
DE9E188B2C3BC92500DB76B4 /* EmptyModel.swift in Sources */,
DDA2EE732C385EB9007C6059 /* MainTabBarController.swift in Sources */,
Expand Down Expand Up @@ -1087,7 +1099,7 @@
DDAF1C902C3D6E3D008A37D3 /* PromiseViewModel.swift in Sources */,
DE254AAA2C31190E00A4015E /* UIStackView+.swift in Sources */,
DE159D362C406E1600425101 /* MyPageViewController.swift in Sources */,
DD4393762C412F4500EC1799 /* EnterInviteCodeView.swift in Sources */,
DD4393762C412F4500EC1799 /* InviteCodeView.swift in Sources */,
DED5DBF02C345317006ECE7E /* BaseCollectionViewCell.swift in Sources */,
DE32D1D42C3BFB56006848DF /* UpdateProfileNameModel.swift in Sources */,
DE6D4D132C3F14D80005584B /* MeetingMemberCell.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"originHash" : "9a47217a5c175e3de895c155c13de64f1d563d13e297ec224a467854897a75be",
"pins" : [
{
"identity" : "abseil-cpp-binary",
Expand Down Expand Up @@ -209,5 +208,5 @@
}
}
],
"version" : 3
"version" : 2
}
2 changes: 1 addition & 1 deletion KkuMulKum/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
self.window?.rootViewController = NicknameViewController()
self.window?.rootViewController = MainTabBarController()
self.window?.makeKeyAndVisible()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
//
// EnterInviteCodeView.swift
// InviteCodeView.swift
// KkuMulKum
//
// Created by YOUJIM on 7/12/24.
//

import UIKit

class EnterInviteCodeView: BaseView {
class InviteCodeView: BaseView {
private let mainTitleLabel: UILabel = UILabel().then {
$0.setText("모임 초대 코드를\n입력해 주세요", style: .head01)
}

private let inviteCodeTextField: CustomTextField = CustomTextField(
let inviteCodeTextField: CustomTextField = CustomTextField(
placeHolder: "모임 초대 코드를 입력해 주세요"
)

let checkImageView: UIImageView = UIImageView().then {
$0.image = .iconCheck.withTintColor(.maincolor)
$0.contentMode = .scaleAspectFit
$0.isHidden = true
}

let errorLabel: UILabel = UILabel().then {
$0.setText("모임 초대 코드를 다시 확인해주세요.", style: .caption02, color: .mainred)
$0.isHidden = true
}

let presentButton: CustomButton = CustomButton(title: "모임 가입하기", isEnabled: false)

override func setupView() {
addSubviews(mainTitleLabel, inviteCodeTextField, presentButton)
backgroundColor = .white

addSubviews(mainTitleLabel, inviteCodeTextField, checkImageView, errorLabel, presentButton)
}

override func setupAutoLayout() {
Expand All @@ -37,6 +50,18 @@ class EnterInviteCodeView: BaseView {
$0.leading.equalTo(mainTitleLabel)
}

checkImageView.snp.makeConstraints {
$0.centerY.equalTo(inviteCodeTextField)
$0.trailing.equalTo(inviteCodeTextField.snp.trailing).inset(12)
$0.height.equalTo(Screen.height(24))
$0.width.equalTo(checkImageView.snp.height)
}

errorLabel.snp.makeConstraints {
$0.top.equalTo(inviteCodeTextField.snp.bottom).offset(8)
$0.leading.equalTo(inviteCodeTextField)
}

presentButton.snp.makeConstraints {
$0.bottom.equalToSuperview().inset(50)
$0.horizontalEdges.equalToSuperview().inset(14)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,88 @@
import UIKit

class EnterInviteCodeViewController: BaseViewController {
private let enterInviteCodeView: EnterInviteCodeView = EnterInviteCodeView()
private let inviteCodeViewModel: InviteCodeViewModel = InviteCodeViewModel()

private let inviteCodeView: InviteCodeView = InviteCodeView()

override func loadView() {
view = enterInviteCodeView
view = inviteCodeView
}

override func viewDidLoad() {
setupBinding()
setupTapGesture()
}

override func setupView() {
setupNavigationBarTitle(with: "내 모임 추가하기")
setupNavigationBarBackButton()
}

private func setupBinding() {
inviteCodeViewModel.inviteCodeState.bind(with: self) { owner, state in
switch state {
case .empty:
self.inviteCodeView.inviteCodeTextField.layer.borderColor = UIColor.gray3.cgColor
self.inviteCodeView.errorLabel.isHidden = true
self.inviteCodeView.checkImageView.isHidden = true
case.selected:
self.inviteCodeView.inviteCodeTextField.layer.borderColor = UIColor.maincolor.cgColor
self.inviteCodeView.errorLabel.isHidden = true
self.inviteCodeView.checkImageView.isHidden = true
case .invalid:
self.inviteCodeView.inviteCodeTextField.layer.borderColor = UIColor.mainred.cgColor
self.inviteCodeView.errorLabel.isHidden = false
self.inviteCodeView.checkImageView.isHidden = true
case .valid:
self.inviteCodeView.inviteCodeTextField.layer.borderColor = UIColor.maincolor.cgColor
self.inviteCodeView.errorLabel.isHidden = true
self.inviteCodeView.checkImageView.isHidden = false
}
}
}

override func setupAction() {
inviteCodeView.inviteCodeTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
inviteCodeView.presentButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside)
}

override func setupDelegate() {
inviteCodeView.inviteCodeTextField.delegate = self
inviteCodeView.inviteCodeTextField.returnKeyType = .done
}

private func setupTapGesture() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))

view.addGestureRecognizer(tapGesture)
}

@objc private func nextButtonTapped() {
// TODO: 서버 연결할 때 데이터 바인딩해서 화면 전환 시키기
let promiseViewController = PromiseViewController()

promiseViewController.modalPresentationStyle = .fullScreen

navigationController?.pushViewController(promiseViewController, animated: true)
}

@objc private func textFieldDidChange(_ textField: UITextField) {
inviteCodeViewModel.validateCode(textField.text ?? "")
}

@objc private func dismissKeyboard() {
view.endEditing(true)
inviteCodeView.inviteCodeTextField.layer.borderColor = UIColor.gray3.cgColor
}
}

extension EnterInviteCodeViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
inviteCodeView.inviteCodeTextField.layer.borderColor = UIColor.gray3.cgColor

return true
}
}

42 changes: 42 additions & 0 deletions KkuMulKum/Source/GroupCreate/ViewModel/InviteCodeViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// InviteCodeViewModel.swift
// KkuMulKum
//
// Created by YOUJIM on 7/12/24.
//

import Foundation

enum InviteCodeState {
case empty
case selected
case valid
case invalid
}

class InviteCodeViewModel {
let inviteCode = ObservablePattern<String>("")
let inviteCodeState = ObservablePattern<InviteCodeState>(.empty)
let codeErrorMessage = ObservablePattern<String?>(nil)
let isNextButtonEnabled = ObservablePattern<Bool>(false)

private let codeRegex = "{1,5}$"

func validateCode(_ code: String) {
inviteCode.value = code

if code.isEmpty {
inviteCodeState.value = .empty
codeErrorMessage.value = nil
isNextButtonEnabled.value = false
} else if code.range(of: codeRegex, options: .regularExpression) != nil {
inviteCodeState.value = .valid
codeErrorMessage.value = nil
isNextButtonEnabled.value = true
} else {
inviteCodeState.value = .invalid
codeErrorMessage.value = "한글, 영문, 숫자만을 사용해 총 5자 이내로 입력해주세요."
isNextButtonEnabled.value = false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class MeetingInfoViewController: BaseViewController {
}

override func setupView() {
setupNavigationBar()
setupNavigationBarBackButton()
}
}

Expand Down Expand Up @@ -150,30 +150,3 @@ extension MeetingInfoViewController: MeetingMemberCellDelegate {
present(viewController, animated: true)
}
}

private extension MeetingInfoViewController {
func setupNavigationBar() {
title = ""

navigationController?.navigationBar.titleTextAttributes = [
.foregroundColor: UIColor.gray8,
.font: UIFont.pretendard(.body03)
]

let backButton = UIBarButtonItem(
image: .iconBack,
style: .plain,
target: self,
action: #selector(backButtonDidTap)
).then {
$0.tintColor = .black
}

navigationItem.leftBarButtonItem = backButton
}

@objc
func backButtonDidTap() {
navigationController?.popViewController(animated: true)
}
}

0 comments on commit af28c01

Please sign in to comment.