Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/NST-10] #38 회원가입 화면 작성 #39

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Noostak_iOS/Noostak_iOS/Domain/UseCase/UserUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// UserUseCase.swift
// Noostak_iOS
//
// Created by 박민서 on 2/20/25.
//

import RxSwift

protocol UserUseCaseProtocol {
func validate(name: String) -> Observable<ValidationResult>
}

final class UserUseCase: UserUseCaseProtocol {
func validate(name: String) -> Observable<ValidationResult> {
return Observable.just(SignUpValidator.validate(name: name))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// SignUpValidator.swift
// Noostak_iOS
//
// Created by 박민서 on 2/20/25.
//

import Foundation

struct SignUpValidator {
static func validate(name: String) -> ValidationResult {
// 1. 글자 수 제한 (1~10자)
guard (1...10).contains(name.count) else {
return .invalid(reason: "이름은 1~10자로 입력해야 합니다.")
}

// 2. 허용 문자: 영어 대소문자, 숫자, 한글, 홀자(한자)
let regex = "^[A-Za-z0-9가-힣]*$"
let predicate = NSPredicate(format: "SELF MATCHES %@", regex)

if !predicate.evaluate(with: name) {
return .invalid(reason: "띄어쓰기 및 특수문자는 사용할 수 없습니다.")
}

return .valid
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// ValidationResult.swift
// Noostak_iOS
//
// Created by 박민서 on 2/20/25.
//


enum ValidationResult {
case valid
case invalid(reason: String)
}
150 changes: 0 additions & 150 deletions Noostak_iOS/Noostak_iOS/Global/Components/AppColorTextField.swift

This file was deleted.

71 changes: 36 additions & 35 deletions Noostak_iOS/Noostak_iOS/Global/Components/AppThemeButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ final class AppThemeButton: UIButton {
private let disposeBag = DisposeBag()
private let theme: AppThemeButton.Theme
private let title: String
private let _isEnabledRelay = BehaviorRelay<Bool>(value: false)

init(theme: Theme = .grayScale, title: String = "다음") {
self.theme = theme
self.title = title
super.init(frame: .zero)
setupUI()
updateUI(for: .disable)
updateUI(isEnabled: false)
bind()
}

required init?(coder: NSCoder) {
Expand All @@ -40,41 +42,23 @@ final class AppThemeButton: UIButton {
self.setTitleColor(theme.foregroundColor, for: .normal)
}

private func updateUI(for state: State) {
self.backgroundColor = theme.backgroundColor(state)
self.isEnabled = state.isEnabled
private func updateUI(isEnabled: Bool) {
self.backgroundColor = theme.backgroundColor(isEnabled)
self.isEnabled = isEnabled
}
}

// MARK: - Interface
extension AppThemeButton {
func bind(state: Observable<AppThemeButton.State>) {
state

private func bind() {
isEnabledRelay
.distinctUntilChanged()
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] state in
self?.updateUI(for: state)
.subscribe(onNext: { [weak self] isEnabled in
self?.updateUI(isEnabled: isEnabled)
})
.disposed(by: disposeBag)
}
}

extension AppThemeButton {

/// 버튼의 활성/비활성 상태
enum State {
case able
case disable

var isEnabled: Bool {
switch self {
case .able:
return true
case .disable:
return false
}
}
}

/// 버튼의 컬러 웨이
enum Theme {
Expand All @@ -85,14 +69,31 @@ extension AppThemeButton {
return .white
}

func backgroundColor(_ state: AppThemeButton.State) -> UIColor {
switch state {

case .able:
return self == .colorScale ? .appBlue600 : .appGray900
case .disable:
return .appGray500
}
func backgroundColor(_ availability: Bool) -> UIColor {
return availability
? self == .colorScale ? .appBlue600 : .appGray900
: .appGray500
}
}
}

// MARK: - Interface
extension AppThemeButton {
var isEnabledRelay: BehaviorRelay<Bool> {
return _isEnabledRelay
}
}

// MARK: Rx Extension
extension Reactive where Base: AppThemeButton {
var isEnabled: ControlProperty<Bool> {
let isEnabledRelay = base.isEnabledRelay

return ControlProperty(
values: isEnabledRelay.asObservable(),
valueSink: Binder(base) { button, state in
button.isEnabledRelay.accept(state)
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ final class ProfileImagePicker: UIView {

cameraButton.do {
$0.setImage(.icnProfileCamera, for: .normal)
$0.isUserInteractionEnabled = true
$0.isUserInteractionEnabled = false
}
}

Expand Down
Loading