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] Generate Nickname View #26

Merged
merged 3 commits into from
Jun 20, 2024
Merged
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
79 changes: 79 additions & 0 deletions Projects/App/Sources/Presentation/Login/Main/LoginButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// LoginButton.swift
// App
//
// Created by 박서연 on 2024/06/18.
// Copyright © 2024 iOS. All rights reserved.
//

import SwiftUI
import DesignSystem

enum Login: String, CaseIterable {
case kakao
case apple

var image: Image {
switch self {
case .kakao:
return ZerosomeAsset.ic_kakako
case .apple:
return ZerosomeAsset.ic_apple
}
}

var backgroundColor: Color {
switch self {
case .kakao:
return Color.kakao
case .apple:
return Color.black
}
}

var titleColor: Color {
switch self {
case .kakao:
return Color.neutral800
case .apple:
return Color.white
}
}

var title: String {
switch self {
case .kakao:
"카카오 ID로 로그인"
case .apple:
"Apple ID로 로그인"
}
}
}

struct LoginButton: View {
let type: Login

init(type: Login) {
self.type = type
}

var body: some View {
HStack(spacing: 0) {
type.image
.frame(width: 24, height: 24)

Text(type.title)
.frame(maxWidth: .infinity, alignment: .center)
}
.padding(.init(top: 15, leading: 15, bottom: 15, trailing: 0))
.applyFont(font: .subtitle1)
.foregroundStyle(type.titleColor)
.background(type.backgroundColor)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
}


#Preview {
LoginButton(type: .apple)
}
60 changes: 60 additions & 0 deletions Projects/App/Sources/Presentation/Login/Main/LoginView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// LoginView.swift
// App
//
// Created by 박서연 on 2024/06/18.
// Copyright © 2024 iOS. All rights reserved.
//

import SwiftUI
import Kingfisher
import DesignSystem



struct LoginView: View {
var body: some View {
ZStack {
Color.primaryFF6972
.ignoresSafeArea()

VStack {
ZerosomeAsset.zero_progress
.frame(width: 240, height: 240)
.clipShape(RoundedRectangle(cornerRadius: 40))

Spacer().frame(height: 123)

VStack(spacing: 12) {
ForEach(Login.allCases, id:\.self) { type in
LoginButton(type: type)
.onTapGesture {
switch type {
case .apple:
print("apple Login")
case .kakao:
print("kakao Login")
}
}
}
}
.padding(.horizontal, 24)
.padding(.bottom, 20)

VStack(spacing: 2) {
Text("일단 둘러볼게요")
.applyFont(font: .body2)
.foregroundStyle(Color.white)
.frame(maxWidth: .infinity, alignment: .center)

DivideRectangle(height: 1, color: .white)
.frame(width: 88)
}
}
}
}
}

#Preview {
LoginView()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// NicknameView.swift
// App
//
// Created by 박서연 on 2024/06/20.
// Copyright © 2024 iOS. All rights reserved.
//
import DesignSystem
import SwiftUI

class NicknameViewModel: ObservableObject {
@Published var isValid: Bool = false

func checkNickName(completion: @escaping (Bool) -> Void) {
// 닉네임 중복체크
}
}

struct NicknameView: View {
@StateObject var viewModel = NicknameViewModel()
@State private var text: String = ""

var body: some View {
VStack(alignment: .leading, spacing: 6) {
Text("닉네임을 설정해 주세요")
.applyFont(font: .heading1)
Text("최소 2자 ~ 12자 이내의 닉네임을 입력해 주세요\n한글/영문/숫자/특수문자 모두 가능해요")
.applyFont(font: .body2)
.foregroundStyle(Color.neutral500)

Spacer().frame(height: 24)

TextInput(text: $text)
.placeholder("닉네임을 입력해 주세요")
.maxCount(12)
.setError(
// TODO: - 서버 통신 및 에러 문구 타이밍 의논
viewModel.isValid
)
.padding(.bottom, 4)

if viewModel.isValid {
Text("사용 가능한 닉네임입니다.")
.applyFont(font: .body4)
.foregroundStyle(Color.positive)
.padding(.leading, 12)
} else {
Text("이미 사용중인 닉네임입니다.")
.applyFont(font: .body4)
.foregroundStyle(Color.negative)
.padding(.leading, 12)
}

Spacer()

CommonButton(title: "회원가입 완료", font: .subtitle1)
.enable(viewModel.isValid)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 22)
.navigationBackButton {
// TODO: - router 추가
}
}
}

#Preview {
NavigationStack {
NicknameView()
}
}
48 changes: 48 additions & 0 deletions Projects/App/Sources/Presentation/Login/Term/SingleTermView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// SingleTermView.swift
// App
//
// Created by 박서연 on 2024/06/19.
// Copyright © 2024 iOS. All rights reserved.
//

import SwiftUI
import DesignSystem

struct SingleTermView: View {
@Binding var isChecked: Bool
var term: Term
var tapTitle: (Term) -> Void

var body: some View {
HStack(spacing: 12) {
(
isChecked
? ZerosomeAsset.ic_check_circle_primary
: ZerosomeAsset.ic_check_circle_gray
)
.resizable()
.frame(width: 24, height: 24)
.onTapGesture {
isChecked.toggle()
}

Text(term.title)
.applyFont(font: .body1)
.foregroundStyle(Color.neutral800)
.frame(maxWidth: .infinity, alignment: .leading)

Spacer()
Text("보기")
.applyFont(font: .body2)
.foregroundStyle(Color.neutral400)
.onTapGesture {
tapTitle(term)
}
}
}
}

#Preview {
SingleTermView(isChecked: .constant(true), term: .term, tapTitle: { _ in print("tapped")})
}
135 changes: 135 additions & 0 deletions Projects/App/Sources/Presentation/Login/Term/TermView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//
// TermView.swift
// App
//
// Created by 박서연 on 2024/06/19.
// Copyright © 2024 iOS. All rights reserved.
//

import Combine
import DesignSystem
import SwiftUI

enum Term: CaseIterable{
case term
case personalInfo
case marketing

var title: String {
switch self {
case .term:
return "(필수) 서비스 이용약관 동의"
case .personalInfo:
return "(필수) 개인정보 처리방침 동의"
case .marketing:
return "(선택) 마케팅 수신 동의"
}
}
}

final class TermViewModel: ObservableObject {
@Published var isAllChecked: Bool = false
@Published var isTermChecked: Bool = false
@Published var isPersonalChecked: Bool = false
@Published var isMarketingChecked: Bool = false

private var cancellables = [AnyCancellable]()

init() {
Publishers.CombineLatest3($isTermChecked, $isPersonalChecked, $isMarketingChecked)
.sink { [weak self] term, personalInfo, marketing in
self?.isAllChecked = term && personalInfo || marketing
}
.store(in: &cancellables)
}

func toggleAll() {
if isTermChecked && isPersonalChecked && isMarketingChecked {
isTermChecked = false
isPersonalChecked = false
isMarketingChecked = false
} else {
isTermChecked = true
isPersonalChecked = true
isMarketingChecked = true
}
}
}

struct TermView: View {
@StateObject var viewModel = TermViewModel()
@State private var isTermChecked: Bool = false

var body: some View {
VStack(alignment:.leading, spacing: 30) {

VStack(alignment:.leading, spacing: 6) {
Text("약관에 동의해 주세요")
.applyFont(font: .heading1)
Text("여러분의 개인정보와 서비스 이용 권리\n잘 지켜드릴게요")
.applyFont(font: .body2)
.foregroundStyle(Color.neutral500)
}

VStack(alignment: .leading, spacing: 18) {
HStack(spacing: 12) {
(
viewModel.isAllChecked
? ZerosomeAsset.ic_check_circle_primary
: ZerosomeAsset.ic_check_circle_gray
)
.onTapGesture {
viewModel.toggleAll()
}

VStack(alignment:.leading, spacing: 2) {
Text("모두 동의")
.applyFont(font: .heading1)
Text("서비스 이용을 위해 아래 약관에 모두 동의합니다.")
.applyFont(font: .body2)
.foregroundStyle(Color.neutral500)
}
}

DivideRectangle(height: 1, color: Color.neutral100)
.padding(.bottom, 3)

SingleTermView(
isChecked: $viewModel.isTermChecked,
term: .term) { term in
print("이용약관 check")
}

SingleTermView(
isChecked: $viewModel.isPersonalChecked,
term: .personalInfo) { term in
print("개인정보 check")
}

SingleTermView(
isChecked: $viewModel.isMarketingChecked,
term: .marketing) { term in
print("마케팅 check")
}
}

Spacer()

CommonButton(title: "다음", font: .subtitle1)
.enable(viewModel.isAllChecked)
.tap {
print("button tapped..")
}
}
.padding(.horizontal, 22)
.navigationBackButton {

}
}
}

#Preview {
NavigationStack {
TermView()
}
}
Loading