diff --git a/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift b/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift index 4af09b2c..20560d41 100644 --- a/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift +++ b/Smeem-iOS/Smeem-iOS/Global/Extensions/SwiftUI/View+.swift @@ -35,7 +35,12 @@ extension View { ) alert.addAction(UIAlertAction(title: firstActionTitle, style: .cancel) { _ in - firstActionHandler?() + // 현재 presenting view controller를 찾아서 dismiss + if let presentedViewController = UIApplication.shared.windows.first?.rootViewController?.presentedViewController { + presentedViewController.dismiss(animated: true) { + firstActionHandler?() + } + } }) alert.addAction(UIAlertAction(title: secondActionTitle, style: .default) { _ in @@ -81,7 +86,8 @@ extension View { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { - window.rootViewController = viewControllerToPresent + let navigationController = UINavigationController(rootViewController: viewControllerToPresent) + window.rootViewController = navigationController UIView.transition(with: window, duration: 0.5, diff --git a/Smeem-iOS/Smeem-iOS/Global/Extensions/UIViewController+.swift b/Smeem-iOS/Smeem-iOS/Global/Extensions/UIViewController+.swift index ff5331ab..7782617d 100644 --- a/Smeem-iOS/Smeem-iOS/Global/Extensions/UIViewController+.swift +++ b/Smeem-iOS/Smeem-iOS/Global/Extensions/UIViewController+.swift @@ -74,8 +74,13 @@ extension UIViewController { func changeRootViewControllerAndPresent(_ viewControllerToPresent: UIViewController) { if let window = UIApplication.shared.windows.first { - window.rootViewController = viewControllerToPresent - UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: nil) + let navigationController = UINavigationController(rootViewController: viewControllerToPresent) + window.rootViewController = navigationController + + UIView.transition(with: window, + duration: 0.5, + options: .transitionCrossDissolve, + animations: nil) } else { viewControllerToPresent.modalPresentationStyle = .overFullScreen self.present(viewControllerToPresent, animated: true, completion: nil) diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingContentView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingContentView.swift index 80036776..36a82153 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingContentView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingContentView.swift @@ -9,8 +9,8 @@ import SwiftUI struct CoachingContentView: View { @Binding var currentIndex: Int - @Binding var coachingsResponse: CoachingsResponse - @Binding var coachingResponse: [CoachingResponse] + @Binding var detailDiaryResponse: DetailDiaryResponse + @Binding var corrections: [CoachingResponse] var body: some View { VStack(spacing: 20.scaledByHeight()) { @@ -19,11 +19,11 @@ struct CoachingContentView: View { .foregroundStyle(Color(UIColor.gray100)) TabView(selection: $currentIndex) { - ForEach(coachingResponse.indices, id: \.self) { item in + ForEach(corrections.indices, id: \.self) { item in ScrollView { VStack(spacing: 8.scaledByHeight()) { - CoachingComparisonView(coachingResponse: $coachingResponse[item]) - CoachingExplanationView(coachingResponse: $coachingResponse[item]) + CoachingComparisonView(coachingResponse: $corrections[item]) + CoachingExplanationView(coachingResponse: $corrections[item]) } } } @@ -32,52 +32,14 @@ struct CoachingContentView: View { .tabViewStyle(.page(indexDisplayMode: .never)) .padding(.horizontal, 16.scaledByWidth()) - PageControl(currentPage: $currentIndex, coachingResponse: $coachingResponse) + PageControl(currentPage: $currentIndex, coachingResponse: $corrections) } } } -#Preview { - @State var defaultIndex: Int = 0 - @State var coachingsResponse = CoachingsResponse.empty - @State var coachingResponse = CoachingsResponse.empty.corrections - CoachingContentView(currentIndex: $defaultIndex, coachingsResponse: $coachingsResponse, coachingResponse: $coachingResponse) -} - -// -//struct CoachingContentView: View { -// @Binding var currentIndex: Int -// @Binding var coachingResponse: CoachingsResponse -// -// var body: some View { -// VStack(spacing: screenHeight * (20 / screenHeight)) { -// Rectangle() -// .frame(height: screenHeight * (8 / screenHeight)) -// .foregroundStyle(Color(UIColor.gray100)) -// -// TabView(selection: $currentIndex) { -// ForEach(coachingResponse.corrections.indices, id: \.self) { item in -// ScrollView { -// VStack(spacing: screenWidth * (8 / screenWidth)) { -// CoachingComparisonView(coachingResponse: $coachingResponse.corrections[item]) -// -// CoachingExplanationView(coachingResponse: $coachingResponse.corrections[item]) -// } -// } -// } -// } -// .frame(width: screenWidth, height: screenHeight * (286 / screenHeight), alignment: .top) -// .tabViewStyle(.page(indexDisplayMode: .never)) -// .padding(.horizontal, screenWidth * (16 / screenWidth)) -// -// PageControl(currentPage: $currentIndex, -// coachingResponse: $coachingResponse) -// } -// } -//} -// //#Preview { // @State var defaultIndex: Int = 0 -// @State var coachingResponse = CoachingsResponse.empty -// CoachingContentView(currentIndex: $defaultIndex, coachingResponse: $coachingResponse) +// @State var coachingsResponse = CoachingsResponse.empty +// @State var coachingResponse = CoachingsResponse.empty.corrections +// CoachingContentView(currentIndex: $defaultIndex, coachingsResponse: $coachingsResponse, coachingResponse: $coachingResponse) //} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CustomSegmentedControl.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CustomSegmentedControl.swift index a231392a..7c18cfa4 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CustomSegmentedControl.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CustomSegmentedControl.swift @@ -23,12 +23,11 @@ struct CustomSegmentedControl: View { ) } } - .background(Color.gray.opacity(0.2)) +// .background(Color.gray.opacity(0.2)) .cornerRadius(6) } private func isCoachingOn(_ index: Int) -> Bool { - // "코칭 ON"일 때만 selected 상태로 처리 return options[index] == "코칭 ON" && selectedIndex == index } } @@ -44,19 +43,19 @@ struct SegmentButton: View { Button(action: action) { Text(title) .padding(.vertical, 8.scaledByHeight()) - .padding(.horizontal, 11.scaledByWidth()) + .padding(.horizontal, 10.scaledByWidth()) .frame(maxWidth: .infinity) .background(backgroundColor) .foregroundColor(foregroundColor) .font(Font(UIFont.c5)) +// .lineLimit(1) +// .minimumScaleFactor(0.9) .overlay( Group { - // 'isSelected'일 때만 'isFirstButton'에 오버레이 추가 if isSelected && isFirstButton { CustomStrokeShape(includeLeadingCorners: false) .stroke(Color(UIColor.gray500), lineWidth: 1) } else if !isSelected { - // 'isSelected'가 아닐 때는 모든 버튼에 대해 모서리 처리 CustomStrokeShape( includeLeadingCorners: isFirstButton, includeTrailingCorners: isLastButton diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/RandomTopicViewSwiftUI.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/RandomTopicViewSwiftUI.swift new file mode 100644 index 00000000..82dc1adb --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/RandomTopicViewSwiftUI.swift @@ -0,0 +1,45 @@ +// +// RandomTopicViewSwiftUI.swift +// Smeem-iOS +// +// Created by Joon Baek on 12/11/24. +// + +import SwiftUI + +struct RandomTopicViewSwiftUI: View { + + // MARK: - Properties + + var contentText: String? + + // MARK: - Body + + var body: some View { + GeometryReader { geometry in + HStack(alignment: .firstTextBaseline, spacing: 3) { + Text("Q.") + .font(Font(UIFont.b1)) + .foregroundColor(Color(UIColor.point)) + .padding(.leading, 18.scaledByWidth()) + + Text(contentText ?? "") + .font(Font(UIFont.b4)) + .foregroundColor(Color(UIColor.smeemBlack)) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.trailing, 30.scaledByWidth()) + } + .padding(.top, 20.scaledByHeight()) + .padding(.bottom, 20.scaledByHeight()) + .background(Color(UIColor.gray100)) + } + .frame(height: contentText?.count ?? 0 > 20 ? 84.scaledByHeight() : 62.scaledByHeight()) + } +} + +#Preview { + var text: String = "랜덤주제 한줄일 경우 " + RandomTopicViewSwiftUI(contentText: text) +} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift index 6a9a7a3b..387097cf 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift @@ -35,19 +35,18 @@ struct SwiftUINavigationView: View { navigationViewModel.leftButtonTapped.send() }, label: { Image("icnBack") - .imageScale(.large) }) .padding(.leading, 12.scaledByWidth()) } else { Spacer().frame(width: 30.scaledByWidth()) } - // 중앙 콘텐츠 (CustomSegmentedControl) + // CustomSegmentedControl if navigationbarType == .diaryDetails { CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) .frame(height: 32.scaledByHeight()) - .padding(.leading, 65.scaledByWidth()) - .padding(.trailing, 59.scaledByWidth()) + .padding(.leading, 64.scaledByWidth()) + .padding(.trailing, 58.scaledByWidth()) } Spacer() @@ -78,6 +77,6 @@ struct SwiftUINavigationView: View { @State var defaultIndex = 0 NavigationView { - SwiftUINavigationView(navigationViewModel: NavigationViewModel(), selectedIndex: $defaultIndex, navigationbarType: .unCoached) + SwiftUINavigationView(navigationViewModel: NavigationViewModel(), selectedIndex: $defaultIndex, navigationbarType: .diaryDetails) } } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Components/CoachingCompleteView.swift b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Components/CoachingCompleteView.swift index 77b9d86b..1758233a 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Components/CoachingCompleteView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Components/CoachingCompleteView.swift @@ -34,7 +34,9 @@ struct CoachingCompleteView: View { highlightIndex: currentIndex )) .font(Font.custom("Pretendard", size: 16)) - .foregroundColor(Color(UIColor.gray400)) + .foregroundColor(coachingAppData.corrections.isEmpty + ? Color(UIColor.black) + : Color(UIColor.gray400)) .lineSpacing(0.375) Spacer() @@ -46,26 +48,29 @@ struct CoachingCompleteView: View { } .padding(.horizontal, screenWidth * 0.048) - VStack(spacing: 20) { - Rectangle() - .frame(height: 8) - .foregroundStyle(Color(UIColor.gray100)) - TabView(selection: $currentIndex) { - ForEach(coachingAppData.corrections.indices, id: \.self) { item in - ScrollView { - VStack(spacing: 8) { - CoachingComparisonView(coachingResponse: $coachingAppData.corrections[item]) - - CoachingExplanationView(coachingResponse: $coachingAppData.corrections[item]) + // 코칭 일기가 있을 때만 보여짐. + if !coachingAppData.corrections.isEmpty { + VStack(spacing: 20) { + Rectangle() + .frame(height: 8) + .foregroundStyle(Color(UIColor.gray100)) + TabView(selection: $currentIndex) { + ForEach(coachingAppData.corrections.indices, id: \.self) { item in + ScrollView { + VStack(spacing: 8) { + CoachingComparisonView(coachingResponse: $coachingAppData.corrections[item]) + + CoachingExplanationView(coachingResponse: $coachingAppData.corrections[item]) + } } } } + .frame(width: screenWidth, height: screenHeight * (326/screenHeight), alignment: .top) + .tabViewStyle(.page(indexDisplayMode: .never)) + + PageControl(currentPage: $currentIndex, + coachingResponse: $coachingAppData.corrections) } - .frame(width: screenWidth, height: screenHeight * (326/screenHeight), alignment: .top) - .tabViewStyle(.page(indexDisplayMode: .never)) - - PageControl(currentPage: $currentIndex, - coachingResponse: $coachingAppData.corrections) } } } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Store/CoachingStore.swift b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Store/CoachingStore.swift index 6ba652f1..7b0b85c0 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Store/CoachingStore.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/Coaching/Store/CoachingStore.swift @@ -64,8 +64,8 @@ final class CoachingStore: Store, ObservableObject { state.hiddenIndex += 1 let coachingResponse = try await service.coachingPostAPI(diaryID: ID) state.coachingAppData = CoachingAppData(diaryText: combineCorrectionText(coachingResponse.corrections), - corrections: filiterCorrection(coachingResponse.corrections), - correctResultText: correctTextResult(coachingResponse.corrections.count)) + corrections: filiterCorrection(coachingResponse.corrections) ?? [], + correctResultText: correctTextResult(filiterCorrection(coachingResponse.corrections) ?? [])) state.hiddenIndex += 1 } catch let error { let error = error as? SmeemError @@ -80,12 +80,12 @@ final class CoachingStore: Store, ObservableObject { return response.map{ $0.originalSentence }.joined(separator: " ") } - func filiterCorrection(_ response: [CoachingResponse]) -> [CoachingResponse] { + func filiterCorrection(_ response: [CoachingResponse]) -> [CoachingResponse]? { return response.filter { $0.isCorrected }.prefix(10).map{$0} } - func correctTextResult(_ count: Int) -> String { - switch count { + func correctTextResult(_ response: [CoachingResponse]) -> String { + switch response.count { case 0: return "완벽한 일기예요!👍\n문장이 자연스럽고 오류가 없어요" case 1: @@ -93,7 +93,7 @@ final class CoachingStore: Store, ObservableObject { case 2...: return "대단해요!🥳🎉\n몇 가지 피드백을 준비해 봤어요." default: - return "대단해요!🥳🎉\n몇 가지 피드백을 준비해 봤어요." + return "완벽한 일기예요!👍\n문장이 자연스럽고 오류가 없어요" } } } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index 3272878d..30fa5300 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -11,10 +11,11 @@ import UIKit struct DetailDiaryCoachedView: View { + // MARK: - Properties + @StateObject private var navigationViewModel = NavigationViewModel() @State private var cancelBag = Set() - @State private var coachingsResponse = CoachingsResponse(corrections: []) @State private var response: DetailDiaryResponse? @State private var isLoading = false @State private var onError = false @@ -24,6 +25,7 @@ struct DetailDiaryCoachedView: View { @State var diaryContent = "" @State var randomTopic = "" @State var currentIndex = 0 + @State private var filteredCorrections: [CoachingResponse] = [] @State private var isShowingFloatingButtons = false @State private var selectedIndex = 0 @State private var navigationbarType: NavigationbarType = .diaryDetails @@ -34,8 +36,10 @@ struct DetailDiaryCoachedView: View { private let detailDiaryService = DetailDiaryService.shared + // MARK: - Body + var body: some View { - VStack(spacing: 16.scaledByWidth()) { + VStack(spacing: 0) { SwiftUINavigationView(navigationViewModel: navigationViewModel, selectedIndex: $selectedIndex, navigationbarType: navigationbarType @@ -54,9 +58,7 @@ struct DetailDiaryCoachedView: View { message: "수정시 모든 코칭 내용이 사라집니다. 그래도 수정하시겠습니까?", firstActionTitle: "취소", secondActionTitle: "확인", - firstActionHandler: { - dismiss() - }, + firstActionHandler: { }, secondActionHandler: { navigateToEditDiary() } @@ -75,10 +77,16 @@ struct DetailDiaryCoachedView: View { } } + if let topic = response?.topic { + if topic != "" { + RandomTopicViewSwiftUI(contentText: response?.topic) + } + } + if let response = response { ScrollableDiaryView( diaryText: response.content, - corrections: response.corrections, + corrections: filteredCorrections, currentIndex: currentIndex, selectedIndex: selectedIndex, dateText: response.createdAt, @@ -86,21 +94,25 @@ struct DetailDiaryCoachedView: View { ) } else { if isLoading { - ProgressView("Loading...") + SmemeEmptyView() + SmemeLoadingView() } } // "코칭 ON"일 때만 표시 if selectedIndex == 1 { - Spacer() - CoachingContentView( - currentIndex: $currentIndex, - coachingsResponse: $coachingsResponse, - coachingResponse: $coachingsResponse.corrections - ) - } else { - Spacer(minLength: 342.scaledByHeight()) - } + Spacer() + CoachingContentView( + currentIndex: $currentIndex, + detailDiaryResponse: Binding( + get: { self.response ?? .empty }, + set: { _ in } + ), + corrections: $filteredCorrections + ) + } else { + Spacer() + } } .onAppear { Task { @@ -128,6 +140,14 @@ struct DetailDiaryCoachedView: View { } } } +} + +// MARK: - Extension + +extension DetailDiaryCoachedView { + func filterCorrection(_ response: DetailDiaryResponse) -> [CoachingResponse] { + return response.corrections.filter { $0.isCorrected }.prefix(10).map{$0} + } private func navigateToEditDiary() { let editVC = EditDiaryViewController() @@ -147,10 +167,9 @@ struct DetailDiaryCoachedView: View { self.diaryContent = response.content self.randomTopic = response.topic - let corrections = response.corrections ?? [] - self.coachingsResponse = CoachingsResponse(corrections: corrections) + self.filteredCorrections = filterCorrection(response) - if corrections.isEmpty { + if filteredCorrections.isEmpty { navigationbarType = .unCoached }