Skip to content

Commit

Permalink
[merge] #34 merge main to branch 34
Browse files Browse the repository at this point in the history
  • Loading branch information
codeJiwon committed Jan 16, 2025
2 parents 10c913f + 2db4d5b commit ce39a9f
Show file tree
Hide file tree
Showing 14 changed files with 540 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// WithSuhyeonLocationSelect.swift
// WithSuhyeon-iOS
//
// Created by 우상욱 on 1/15/25.
//

import SwiftUI

struct WithSuhyeonLocationSelect: View {
let withSuhyeonLocation: [WithSuhyeonLocation]
let selectedMainLocationIndex: Int
let selectedSubLocationIndex: Int
let onTabSelected: (Int, Int) -> Void

var body: some View {
HStack(spacing: 0) {
ScrollView {
VStack(spacing: 0) {
ForEach(withSuhyeonLocation.indices) { index in
ZStack {
Text(withSuhyeonLocation[index].location)
.font(.body02SB)
.foregroundColor(selectedMainLocationIndex == index ? Color.white : Color.black)
.padding(.vertical, 9)
.padding(.leading, 12)
.padding(.trailing, 42)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(selectedMainLocationIndex == index ? Color.primary500 : Color.clear)
)
}
.frame(height: 50)
.contentShape(Rectangle())
.onTapGesture {
if(selectedMainLocationIndex != index) {
onTabSelected(index, 0)
}
}
}
}.padding(.leading, 16)
.padding(.trailing, 8)
}
Divider()
.foregroundColor(.gray100)
ScrollView {
VStack(alignment: .leading, spacing: 0) {
ForEach(withSuhyeonLocation[selectedMainLocationIndex].subLocation.indices) { index in
ZStack(alignment: .leading) {
HStack {
Text(withSuhyeonLocation[selectedMainLocationIndex].subLocation[index])
.font(.body03SB)
.foregroundColor(.gray500)
Spacer()
}
.padding(.leading, 12)
.padding(.vertical, 10)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(selectedSubLocationIndex == index ? Color.primary50 : Color.clear)
)
.frame(width: .infinity)
.contentShape(Rectangle())
.onTapGesture {
onTabSelected(selectedMainLocationIndex, index)
}
}
.frame(height: 50)
.padding(.leading, 8)
.padding(.trailing, 16)
}
}
}
}.frame(height: .infinity)
}
}

struct locationPreview: View {
@State var selectedMainLocationIndex: Int = 0
@State var selectedSubLocationIndex: Int = 0

var body: some View {
VStack {
WithSuhyeonLocationSelect(withSuhyeonLocation: WithSuhyeonLocation.location, selectedMainLocationIndex: selectedMainLocationIndex, selectedSubLocationIndex: selectedSubLocationIndex, onTabSelected: { num1, num2 in
selectedMainLocationIndex = num1
selectedSubLocationIndex = num2
})
}.frame(height: 400)
}
}

#Preview {
locationPreview()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// WithSuhyeonLocation.swift
// WithSuhyeon-iOS
//
// Created by 우상욱 on 1/15/25.
//

import Foundation

struct WithSuhyeonLocation: Identifiable {
let id: UUID = UUID()
let location: String
let subLocation: [String]
}

extension WithSuhyeonLocation {
static let location = [WithSuhyeonLocation(location: "서울", subLocation: ["서울1","서울2","서울3","서울4","서울5","서울6","서울7","서울8","서울9","서울10","서울11"]),WithSuhyeonLocation(location: "부산", subLocation: ["부산1","부산2","부산3","부산4","부산5","부산6","부산7","부산8","부산9","부산10","부산11",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),]
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// SignUpContent.swift
// WithSuhyeon-iOS
//
// Created by 김예지 on 1/15/25.
//

import SwiftUI

struct SignUpContent: View {
@Binding var selectedTab: SignUpContentCase
init(selectedTab: Binding<SignUpContentCase>) {
self._selectedTab = selectedTab
UITabBar.appearance().isHidden = true
}

var body: some View {
TabView(selection: $selectedTab) {
TermsOfServiceView()
.tag(SignUpContentCase.termsOfServiceView)
PhoneAuthenticationView()
.tag(SignUpContentCase.authenticationView)
WriteNickNameView()
.tag(SignUpContentCase.nickNameView)
SelectBirthYearView()
.tag(SignUpContentCase.birthYearView)
SelectGenderView()
.tag(SignUpContentCase.genderView)
ProfileImageView()
.tag(SignUpContentCase.profileImageView)
ActiveAreaView()
.tag(SignUpContentCase.activeAreaView)
}
.tabViewStyle(PageTabViewStyle())
.onAppear {
UIScrollView.appearance().isScrollEnabled = false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// SignUpContentCase.swift
// WithSuhyeon-iOS
//
// Created by 김예지 on 1/15/25.
//

import Foundation

public enum SignUpContentCase: CaseIterable {
case termsOfServiceView
case authenticationView
case nickNameView
case birthYearView
case genderView
case profileImageView
case activeAreaView

var title: String {
switch self {
case .termsOfServiceView:
return "수현이랑\n서비스 이용약관"
case .authenticationView:
return "본인인증을 위한\n휴대폰 번호 인증이 필요해요"
case .nickNameView:
return "수현이랑에서 사용할\n닉네임을 입력해주세요"
case .birthYearView:
return "태어난 년도를\n선택해주세요"
case .genderView:
return "성별을\n선택해주세요"
case .profileImageView:
return "프로필 이미지를\n등록해주세요"
case .activeAreaView:
return "자주 활동하는\n지역을 선택해주세요"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// SignUpFeature.swift
// WithSuhyeon-iOS
//
// Created by 김예지 on 1/15/25.
//

import Foundation
import Combine

class SignUpFeature: Feature {
struct State {
var progress: Double = 0.0
var isAgree: Bool = false
var buttonState: WithSuhyeonButtonState = .disabled
}

enum Intent {
case tapButton
case tapBackButton
}

enum SideEffect {

}

@Published private(set) var state = State()
@Published var currentContent: SignUpContentCase = .termsOfServiceView
private var cancellables = Set<AnyCancellable>()
private let intentSubject = PassthroughSubject<Intent, Never>()
let sideEffectSubject = PassthroughSubject<SideEffect, Never>()

init() {
bindIntents()
updateProgress()
receiveState()
}

private func bindIntents() {
intentSubject.sink { [weak self] intent in
self?.handleIntent(intent)
}.store(in: &cancellables)
}

private func receiveState() {
$state.sink { [weak self] _ in
DispatchQueue.main.async {
self?.updateButtonState()
}
}.store(in: &cancellables)

$currentContent.sink { [weak self] _ in
DispatchQueue.main.async {
self?.updateButtonState()
}
}.store(in: &cancellables)
}

func send(_ intent: Intent) {
intentSubject.send(intent)
}

func handleIntent(_ intent: Intent) {
switch intent {
case .tapButton:
moveToNextStep()
case .tapBackButton:
moveToPreviousStep()
}
}

private func moveToNextStep() {
if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent),
currentIndex < SignUpContentCase.allCases.count - 1 {
currentContent = SignUpContentCase.allCases[currentIndex + 1]
updateProgress()
}
}

private func moveToPreviousStep() {
if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent),
currentIndex > 0 {
currentContent = SignUpContentCase.allCases[currentIndex - 1]
updateProgress()
}
}

private func updateProgress() {
if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent) {
state.progress = Double(currentIndex + 1) / Double(SignUpContentCase.allCases.count) * 100
}
}

private func updateButtonState() {
let newButtonState: WithSuhyeonButtonState

switch currentContent {
case .termsOfServiceView:
newButtonState = state.isAgree ? .enabled : .disabled
default:
newButtonState = .enabled
}

guard state.buttonState != newButtonState else {
return
}

state.buttonState = newButtonState
}

func changeSelectedContent(signUpContentCase: SignUpContentCase) {
currentContent = signUpContentCase
}

func updateIsAgree(_ newValue: Bool) {
state.isAgree = newValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// SignUpView.swift
// WithSuhyeon-iOS
//
// Created by 김예지 on 1/15/25.
//

import SwiftUI

struct SignUpView: View {
@EnvironmentObject private var router: RouterRegistry
@StateObject private var signUpFeature = SignUpFeature()

var body: some View {
VStack(alignment: .leading, spacing: 0) {
WithSuhyeonTopNavigationBar(
title: "",
onTapLeft: {
signUpFeature.send(
.tapBackButton
)
})

WithSuhyeonProgressBar(progress: signUpFeature.state.progress)

Text(signUpFeature.currentContent.title)
.font(.title02B)
.padding(.leading, 16)
.padding(.vertical, 20)

SignUpContent(selectedTab: $signUpFeature.currentContent)

WithSuhyeonButton(
title: "다음",
buttonState: signUpFeature.state.buttonState,
clickable: signUpFeature.state.buttonState == .enabled,
onTapButton: {
signUpFeature.send(.tapButton)
}
)
.padding(.horizontal, 16)
}.environmentObject(signUpFeature)
}
}

#Preview {
SignUpView()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// ActiveAreaView.swift
// WithSuhyeon-iOS
//
// Created by 김예지 on 1/15/25.
//

import SwiftUI

struct ActiveAreaView: View {
var body: some View {
Text("활동 지역 선택")
}
}

#Preview {
ActiveAreaView()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// PhoneAuthenticationView.swift
// WithSuhyeon-iOS
//
// Created by 김예지 on 1/15/25.
//

import SwiftUI

struct PhoneAuthenticationView: View {
var body: some View {
Text("핸드폰 인증")
}
}

#Preview {
PhoneAuthenticationView()
}
Loading

0 comments on commit ce39a9f

Please sign in to comment.