diff --git a/Dietto/Dietto.xcodeproj/project.pbxproj b/Dietto/Dietto.xcodeproj/project.pbxproj index 2fb9184..2f83443 100644 --- a/Dietto/Dietto.xcodeproj/project.pbxproj +++ b/Dietto/Dietto.xcodeproj/project.pbxproj @@ -270,7 +270,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = UUYBHY988H; + DEVELOPMENT_TEAM = 5664C7G97A; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Dietto/Info.plist; @@ -303,7 +303,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = UUYBHY988H; + DEVELOPMENT_TEAM = 5664C7G97A; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Dietto/Info.plist; diff --git a/Dietto/Dietto/Presentation/Article/View/Interests.swift b/Dietto/Dietto/Presentation/Article/View/Interests.swift deleted file mode 100644 index 19a238c..0000000 --- a/Dietto/Dietto/Presentation/Article/View/Interests.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// Interests.swift -// Dietto -// -// Created by 안세훈 on 5/26/25. -// - -import SwiftUI - -struct Interests: View { - var topic: String - var titles: [String] - var onClicked: (String) -> Void - var isSelected: (String) -> Bool - - @State private var contentHeight: CGFloat = 0 - - var body: some View { - VStack(alignment: .leading, spacing: 8) { - Text(topic) - .font(.pretendardBold16) - - FlowLayout(spacing: 8, lineSpacing: 8, contentHeight: $contentHeight) { - ForEach(titles, id: \.self) { title in - let selected = isSelected(title) - - Button(action: { - onClicked(title) - }) { - Text(title) - .font(.pretendardBold14) - .foregroundColor(selected ? .white : .text) - .padding(.horizontal, 10) - .padding(.vertical, 4) - } - .background( - Capsule().fill(selected ? Color.appMain : Color.backGround) - ) - .overlay( - Capsule().stroke(Color.appMain, lineWidth: 1) - ) - .fixedSize() - } - } - } - } -} diff --git a/Dietto/Dietto/Presentation/Dietary/View/InterestView.swift b/Dietto/Dietto/Presentation/Dietary/View/InterestView.swift new file mode 100644 index 0000000..bf7ba8e --- /dev/null +++ b/Dietto/Dietto/Presentation/Dietary/View/InterestView.swift @@ -0,0 +1,49 @@ +//// +//// InterestsView.swift +//// Dietto +//// +//// Created by InTak Han on 5/14/25. +//// +// +//import SwiftUI +// +//struct InterestsView: View { +// +// @ObservedObject var viewModel : OnboardingViewModel +// +// @Binding var selection: Int //binding +// +// var body: some View { +// VStack(spacing: 40) { +// VStack(alignment: .leading) { +// Text("\(viewModel.name)님의") +// + Text(" 관심사").foregroundColor(.appMain) +// + Text("를 알려주세요 !") +// } +// .font(.pretendardBold24) +// .padding(.top, 20) +// .padding(.horizontal, 20) +// .frame(maxWidth: .infinity, alignment: .leading) +// +// ScrollView { +// VStack(alignment: .leading, spacing: 30) { +// ForEach(viewModel.interestData, id: \.topic) { block in +// Interests( +// topic: block.topic, +// titles: block.titles, +// onClicked: { title in +// viewModel.toggleInterest(title) +// }, +// isSelected: { title in +// viewModel.selectedArticles.contains(where: { $0.title == title }) +// } +// ) +// } +// } +// .padding(.horizontal, 20) +// .padding(.bottom, 20) +// } +// } +// .background(Color(.backGround).ignoresSafeArea()) +// } +//} diff --git a/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift b/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift index b7643b6..6b94b16 100644 --- a/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift +++ b/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift @@ -21,7 +21,7 @@ struct GoalView: View { VStack(spacing: 40) { VStack(alignment: .leading, spacing: 4) { - Text("\(viewModel.nickname)님의") + Text("\(viewModel.name)님의") Text("목표").foregroundColor(.appMain) + Text("를 알려주세요 !") } .font(.pretendardBold24) diff --git a/Dietto/Dietto/Presentation/Onboarding/View/IntroView.swift b/Dietto/Dietto/Presentation/Onboarding/View/IntroView.swift index f815243..616c09f 100644 --- a/Dietto/Dietto/Presentation/Onboarding/View/IntroView.swift +++ b/Dietto/Dietto/Presentation/Onboarding/View/IntroView.swift @@ -8,52 +8,76 @@ import SwiftUI struct IntroView: View { - - @State private var first : Bool = false - @State private var second : Bool = false - - @State private var isAnimating : Bool = false + @State private var first: Bool = false + @State private var second: Bool = false + @State private var nextbtn: Bool = false + @State private var navigate: Bool = false // ✅ push 트리거 var body: some View { - ZStack { - if first { - Text("안녕하세요! 환영합니다 !") - .foregroundColor(.appMain) - .font(.pretendardBlack24) - .opacity(first ? 1 : 0) - .transition(.opacity) - .animation(.easeInOut(duration: 1), value: first) - } - - if second { - Text("프로필을 설정해주세요!") - .foregroundColor(.appMain) - .font(.pretendardBlack24) - .opacity(second ? 1 : 0) - .transition(.opacity) - .animation(.easeInOut(duration: 1), value: second) + NavigationStack { + ZStack { + Color(.backGround) + .ignoresSafeArea() + + // 텍스트는 항상 중앙에 고정 + ZStack { + if first { + Text("안녕하세요! 환영합니다 !") + .foregroundColor(.appMain) + .font(.pretendardBlack24) + .transition(.opacity) + } + + if second { + Text("프로필을 설정해주세요!") + .foregroundColor(.appMain) + .font(.pretendardBlack24) + .transition(.opacity) + } + } + .animation(.easeInOut(duration: 1), value: first) + .animation(.easeInOut(duration: 1), value: second) + + VStack { + + Spacer() + + if nextbtn { + NavigationLink(destination: TutorialView() + .navigationBarBackButtonHidden(true)) { + Text("다음") + .frame(maxWidth: .infinity) + .padding() + .background(Color.appMain) + .foregroundColor(.white) + .cornerRadius(13) + .font(.pretendardMedium16) + } + .padding(.horizontal, 20) + .padding(.bottom, 20) + .transition(.opacity) + } + } } + .animation(.easeInOut(duration: 1), value: nextbtn) } .onAppear { - withAnimation(.easeInOut(duration: 1)) { - first = true - } + first = true - DispatchQueue.main.asyncAfter(deadline: .now()) { - withAnimation(.easeInOut(duration: 1)) { - first = false - } + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + first = false } DispatchQueue.main.asyncAfter(deadline: .now() + 3) { - withAnimation(.easeInOut(duration: 1)) { - second = true - } + second = true + nextbtn = true } } } } + + #Preview { IntroView() } diff --git a/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift b/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift new file mode 100644 index 0000000..86f57fa --- /dev/null +++ b/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift @@ -0,0 +1,142 @@ +// +// ProfileEditView.swift +// Dietto +// +// Created by kyuhyeon Lee on 5/15/25. +// + +import SwiftUI +import Foundation + + +struct ProfileEditView: View { + @Binding var selection: Int + @ObservedObject var viewModel: OnboardingViewModel + @State private var numberInput: String = "" + + var body: some View { + ZStack { + Color(.backGround) + .ignoresSafeArea() + + VStack(spacing: 30) { + // MARK: - 프로필 이미지 + ZStack(alignment: .bottomTrailing) { + Button { viewModel.showPhotoSheet.toggle() } label: { + Circle() + .stroke(Color.appMain, lineWidth: 2) + .frame(width: 180, height: 180) + .background( + Image(systemName: "person.crop.circle.fill") + .resizable() + .scaledToFit() + .foregroundColor(.gray) + .frame(width: 180, height: 180) + ) + } + Button { viewModel.showPhotoSheet.toggle() } label: { + Circle() + .fill(Color.black) + .frame(width: 45, height: 45) + .overlay( + Image(systemName: "camera") + .font(.system(size: 20, weight: .bold)) + .foregroundColor(.white) + ) + } + .offset(x: -5, y: -5) + } + + // MARK: - 이름 입력란 + VStack(alignment: .leading, spacing: 6) { + Text("이름") + .font(.pretendardBold16) + TextField("이름을 입력해주세요", text: $viewModel.name) + .font(.pretendardMedium16) + .padding(.horizontal, 20) + .frame(height: 54) + .background( + RoundedRectangle(cornerRadius: 20) + .stroke(Color.appMain, lineWidth: 2) + ) + } + + // MARK: - 성별 선택 + VStack(alignment: .leading, spacing: 6) { + Text("성별") + .font(.pretendardBold16) + ZStack { + RoundedRectangle(cornerRadius: 20) + .stroke(Color.appMain, lineWidth: 2) + .frame(height: 54) + + HStack { + Text(viewModel.gender.isEmpty ? "남성" : viewModel.gender) + .font(.pretendardMedium16) + .foregroundColor(viewModel.gender.isEmpty ? .black : .primary) + .padding(.leading, 20) + + Spacer() + + Menu { + Button { viewModel.selectGender("남성") } label: { Text("남성") } + Button { viewModel.selectGender("여성") } label: { Text("여성") } + } label: { + Image(systemName: "chevron.up.chevron.down") + .font(.pretendardBold16) + .foregroundColor(.gray) + .padding(.trailing, 12) + } + } + } + } + + // MARK: - 키 수정 + VStack(alignment: .leading, spacing: 6) { + Text("키") + .font(.pretendardBold16) + HStack(spacing: 0) { + TextField("170", text: $numberInput) + .keyboardType(.numberPad) + .multilineTextAlignment(.center) + .frame(height: 54) + .padding(.horizontal, 20) + .background( + RoundedRectangle(cornerRadius: 20) + .stroke(Color.appMain, lineWidth: 2) + ) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("완료") { + UIApplication.shared.sendAction( + #selector(UIResponder.resignFirstResponder), + to: nil, from: nil, for: nil + ) + } + } + } + Text("cm") + .font(.custom("NerkoOne-regular", size: 50)) + .foregroundColor(Color.appMain) + .padding(.leading, 12) + } + } + + Spacer() + } + .padding(.top, 48) + .padding(.horizontal, 20) + .confirmationDialog( + "프로필 사진 선택", + isPresented: $viewModel.showPhotoSheet, + titleVisibility: .visible + ) { + Button("카메라") { /* 카메라 액션 */ } + Button("갤러리") { /* 갤러리 액션 */ } + Button("취소", role: .cancel) {} + } + } + + } +} diff --git a/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift b/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift index 0a3aaa4..c03c3bf 100644 --- a/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift +++ b/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift @@ -9,19 +9,24 @@ import SwiftUI struct TutorialView: View { + @AppStorage("isFirstLaunch") var isFirstLaunch: Bool = true + @State private var selection = 0 @StateObject private var viewModel = OnboardingViewModel() + @Environment(\.dismiss) private var dismiss + var body: some View { ZStack { Color(.backGround).ignoresSafeArea(edges: .all) VStack { TabView(selection: $selection) { + + ProfileEditView(selection: $selection, viewModel: viewModel) + .tag(0) //목표 설정 뷰 GoalView(selection: $selection, viewModel: viewModel) - .tag(0) -// InterestsView(viewModel: viewModel, selection: $selection) -// .tag(1) + .tag(1) } .tabViewStyle(.page(indexDisplayMode: .never)) .animation(.linear, value: selection) @@ -33,14 +38,13 @@ struct TutorialView: View { HStack { Button { - if selection == 0 { - viewModel.setGoals(weight: viewModel.weight, distance: viewModel.distance) - } if selection < 1 { selection += 1 + }else{ + isFirstLaunch = false } } label: { - Text("다음") + Text(selection < 1 ? "다음" : "완료") .frame(maxWidth: .infinity) .padding() .background(Color.appMain) diff --git a/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift b/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift index d2f2d2b..882f467 100644 --- a/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift +++ b/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift @@ -9,17 +9,29 @@ import SwiftUI final class OnboardingViewModel: ObservableObject { - let nickname: String = "asdasdasd" + + @Published var name: String = "name" + @Published var birthString: String = "birthString" + @Published var gender: String = "남성" + @Published var weight: Int = 60 + @Published var distance: Int = 60 + @Published var showPhotoSheet: Bool = false + @Published var showDatePicker: Bool = false let weights: [Int] = Array(20...100).reversed() let distances: [Int] = Array(1...10).reversed() + //MARK: - 프로필 설정 + func saveProfile() { + print("프로필이 저장되었습니다.") + print("이름: \(name), 생일: \(birthString), 성별: \(gender), 몸무게: \(weight), 거리: \(distance)") + } - - @Published var weight : Int = 0 - @Published var distance : Int = 0 + func selectGender(_ gender: String) { + self.gender = gender + } //MARK: - 목표 설정 @@ -28,5 +40,26 @@ final class OnboardingViewModel: ObservableObject { self.distance = distance } - + // MARK: - 관심사 추가 / 삭제 +// +// func addInterest(_ title: String) { +// let entity = ArticleEntity(title: title) +// guard !selectedArticles.contains(where: { $0.title == title }) else { return } +// selectedArticles.append(entity) +// } +// +// func removeInterest(_ title: String) { +// if let index = selectedArticles.firstIndex(where: { $0.title == title }) { +// selectedArticles.remove(at: index) +// } +// } +// +// func toggleInterest(_ title: String) { +// if selectedArticles.contains(where: { $0.title == title }) { +// removeInterest(title) +// } else { +// addInterest(title) +// } +// print(selectedArticles) +// } } diff --git a/Dietto/Dietto/Presentation/Profile/View/ProfileEditView.swift b/Dietto/Dietto/Presentation/Profile/View/ProfileEditView.swift deleted file mode 100644 index d027eeb..0000000 --- a/Dietto/Dietto/Presentation/Profile/View/ProfileEditView.swift +++ /dev/null @@ -1,207 +0,0 @@ -// -// ProfileEditView.swift -// Dietto -// -// Created by kyuhyeon Lee on 5/15/25. -// - -import SwiftUI -import Foundation -//MARK: - 뷰 -class ProfileEditViewModel: ObservableObject { - - @Published var name: String = "" - @Published var birthString: String = "" - @Published var gender: String = "" - @Published var weight: String = "" - @Published var showPhotoSheet: Bool = false - @Published var showDatePicker: Bool = false - - func saveProfile() { - print("프로필이 저장되었습니다.") - } - - func selectGender(_ gender: String) { - self.gender = gender - } -} - -struct ProfileEditView: View { - @StateObject private var viewModel = ProfileEditViewModel() - @Environment(\.dismiss) private var dismiss - @State private var numberInput: String = "" - - var body: some View { - ZStack { - Color(.backGround).ignoresSafeArea() - VStack { - // MARK: - 프로필 이미지 - 버튼 - ZStack(alignment: .bottomTrailing) { - Button(action: { - viewModel.showPhotoSheet = true - }) { - Circle() - .stroke(Color.appMain, lineWidth: 2) - .frame(width: 180, height: 180) - .background( - Image(systemName: "person.crop.circle.fill") - .resizable() - .scaledToFit() - .foregroundColor(Color(.systemGray4)) - .frame(width: 180, height: 180) - ) - } - //MARK: - 카메라 버튼 - 버튼 - Button(action: { - viewModel.showPhotoSheet = true - }) { - ZStack { - Circle() - .fill(Color.black) - .frame(width: 45, height: 45) - Image(systemName: "camera") - .foregroundColor(.white) - .font(.system(size: 20, weight: .bold)) - } - } - .offset(x: -1, y: -1) - } - .frame(width: 180, height: 180) - .padding(.top, 48) - - // MARK: - 이름 입력란 - VStack(alignment: .leading, spacing: 6) { - Text("이름") - .font(.pretendardBold16) - .padding(.leading, 12) - .frame(maxWidth: .infinity, alignment: .leading) - TextField("이름을 입력해주세요", text: $viewModel.name) - .font(.pretendardMedium16) - .padding(.horizontal, 20) - .frame(height: 54) - .background( - RoundedRectangle(cornerRadius: 20) - .stroke(Color.appMain, lineWidth: 2) - ) - } - .padding(.top, 40) - .padding(.horizontal, 20) - - - //MARK: - 성별 선택 - VStack(alignment: .leading, spacing: 6) { - Text("성별") - .font(.pretendardBold16) - .padding(.leading, 12) - .frame(maxWidth: .infinity, alignment: .leading) - - ZStack { - RoundedRectangle(cornerRadius: 20) - .stroke(.appMain, lineWidth: 2) - .frame(height: 54) - - HStack { - Text(viewModel.gender.isEmpty ? "남성" : viewModel.gender) - .font(.pretendardMedium16) - .foregroundColor(viewModel.gender.isEmpty ? .black : .primary) - .padding(.leading, 20) - - Spacer() - - Menu { - Button { - viewModel.selectGender("남성") - } label: { - Text("남성") - } - Button { - viewModel.selectGender("여성") - } label: { - Text("여성") - } - } label: { - Image(systemName: "chevron.up.chevron.down") - .font(.pretendardBold16) - .foregroundColor(.gray) - .padding(.trailing, 12) - } - .menuStyle(.automatic) - } - } - } - .padding(.top, 30) - .padding(.horizontal, 20) - - //MARK: - 키 수정 - VStack(alignment: .leading, spacing: 1) { - Text("키") - .font(.pretendardBold16) - .padding(.leading, 12) - .frame(maxWidth: .infinity, alignment: .leading) - - HStack(spacing: 0) { - TextField("170", text: $numberInput) - .keyboardType(.numberPad) - .multilineTextAlignment(.center) - .frame(height: 54) - .padding(.horizontal, 20) - .background( - RoundedRectangle(cornerRadius: 20) - .stroke(Color.appMain, lineWidth: 2) - ) - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - Spacer() - Button("완료") { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } - } - } - Text("cm") - .font(.custom("NerkoOne-regular", size: 50)) - .foregroundColor(Color.appMain) - .padding(.leading, 12) - } - } - .padding(.horizontal, 25) - .padding(.top, 30) - - //MARK: - 저장 버튼 - Button(action: { - dismiss() - }) { - Text("저장") - .font(.pretendardBold16) - .foregroundColor(.white) - .frame(maxWidth: .infinity) - .padding(.vertical, 15) - .background( - RoundedRectangle(cornerRadius: 20) - .fill(Color.appMain) - ) - } - .padding(.horizontal, 20) - .padding(.top, 80) - .padding(.bottom, 40) - } - } - //MARK: - 프로필 사진 변경 - .navigationTitle("프로필 수정") - .navigationBarTitleDisplayMode(.inline) - - // MARK: - 프로필 사진 변경 시트 - .confirmationDialog( - "프로필 사진 선택", - isPresented: $viewModel.showPhotoSheet, - titleVisibility: .visible - ) { - Button("카메라"){} - Button("갤러리"){} - Button("취소", role: .cancel) {} - } - } -} - -#Preview { - ProfileEditView() -} diff --git a/Dietto/Dietto/Presentation/Profile/View/ProfileView.swift b/Dietto/Dietto/Presentation/Profile/View/ProfileView.swift index d622cda..385373f 100644 --- a/Dietto/Dietto/Presentation/Profile/View/ProfileView.swift +++ b/Dietto/Dietto/Presentation/Profile/View/ProfileView.swift @@ -111,7 +111,7 @@ struct ProfileView: View { } //MARK: - edit 누르면 ProfileEditView로 이동 .navigationDestination(isPresented: $viewModel.isEditActive) { - ProfileEditView() +// ProfileEditView() } } .padding(.horizontal, 24)