From ea61a9189e9ee4a5baa69da80c87a57e73e89548 Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Thu, 21 Nov 2024 13:00:51 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[Feat]=20#222=20-=20DetailDiaryCoachedView?= =?UTF-8?q?=20=EC=83=81=EB=8B=A8=20TextView=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 4 + .../DetailDiary/DetailDiaryCoachedView.swift | 79 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 46427dd3..734ad0e4 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 37A574E62A02178D00312453 /* SmeemToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A574E52A02178D00312453 /* SmeemToastView.swift */; }; 37A574E82A02424F00312453 /* RandomTopicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A574E72A02424F00312453 /* RandomTopicView.swift */; }; 37ADCC022B0289AD00E474AA /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37ADCC012B0289AD00E474AA /* Observable.swift */; }; + 37B123B62CEEE58E00ED973A /* DetailDiaryCoachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B123B52CEEE58E00ED973A /* DetailDiaryCoachedView.swift */; }; 37B360AA2AC2A4680006C8ED /* SmeemTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B360A92AC2A4680006C8ED /* SmeemTextView.swift */; }; 37BB09BB2CBF8BA800A3B28A /* CoachingCompletedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */; }; 37BCADF22BC3FFBF006EF960 /* DetailDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */; }; @@ -313,6 +314,7 @@ 37A574E52A02178D00312453 /* SmeemToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemToastView.swift; sourceTree = ""; }; 37A574E72A02424F00312453 /* RandomTopicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomTopicView.swift; sourceTree = ""; }; 37ADCC012B0289AD00E474AA /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; + 37B123B52CEEE58E00ED973A /* DetailDiaryCoachedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryCoachedView.swift; sourceTree = ""; }; 37B360A92AC2A4680006C8ED /* SmeemTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemTextView.swift; sourceTree = ""; }; 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingCompletedView.swift; sourceTree = ""; }; 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryViewModel.swift; sourceTree = ""; }; @@ -806,6 +808,7 @@ children = ( 37DCA65F2A48033400FF8F90 /* DetailDiaryViewController.swift */, 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */, + 37B123B52CEEE58E00ED973A /* DetailDiaryCoachedView.swift */, ); path = DetailDiary; sourceTree = ""; @@ -2032,6 +2035,7 @@ 37DCA65D2A475B5100FF8F90 /* RandomTopicResponse.swift in Sources */, 4ABCBCCC2BDE8E44003138A8 /* MySummaryService.swift in Sources */, 371107DA2AC99099007A4AC2 /* KeyboardFollowingLayoutHandler.swift in Sources */, + 37B123B62CEEE58E00ED973A /* DetailDiaryCoachedView.swift in Sources */, 4A9FAB452A4C3D1200C40D5A /* BadgeListViewController.swift in Sources */, 4ABCBCE52BE25D82003138A8 /* TrainingPlanViewController.swift in Sources */, 4AB7C9192B75F9B500845733 /* GesturePublisher+.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift new file mode 100644 index 00000000..cc8a8631 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -0,0 +1,79 @@ +// +// DetailDiaryCoachedView.swift +// Smeem-iOS +// +// Created by Joon Baek on 11/21/24. +// + +import SwiftUI + +struct SentenceData: Identifiable { + let id = UUID() + let text: String + let isCorrect: Bool +} + +struct DetailDiaryCoachedView: View { + var attributedText: AttributedString { + var result = AttributedString("") + + for (index, sentence) in MockData.sentences.enumerated() { + var attributedSentence = AttributedString(sentence.text) + + // 개별 문장 스타일 지정 + attributedSentence.foregroundColor = sentence.isCorrect ? UIColor.gray400 : UIColor.smeemWhite + attributedSentence.backgroundColor = sentence.isCorrect ? nil : UIColor.point + attributedSentence.font = .custom("Pretendard", size: 16) + + // 문장 추가 + result += attributedSentence + + // 마지막 문장이 아니면 공백 추가 + if index < MockData.sentences.count - 1 { + result += AttributedString(" ") + } + } + + return result + } + + var body: some View { + SwiftUINavigationView() + + VStack(spacing: 16) { + Text(MockData.headerText) + .font(Font.custom("Pretendard", size: 16)) + .foregroundColor(Color(UIColor.smeemBlack)) + .frame(maxWidth: .infinity, alignment: .leading) + + ScrollView { + Text(attributedText) + .frame(maxWidth: .infinity, alignment: .leading) + } + .frame(height: screenHeight * 0.32) + + Spacer() + } + .padding(.horizontal, screenWidth * 0.048) + } +} + +extension DetailDiaryCoachedView { + struct MockData { + static let headerText = "일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?" + + static let sentences: [SentenceData] = [ + SentenceData(text: "I watched Avatar with my boyfriend at Hongdae CGV.", isCorrect: true), + SentenceData(text: "I should have skimmed the previous season what they were saying and the universe(??).", isCorrect: false), + SentenceData(text: "What I was annoyed then was 두팔 didn't know that as me.", isCorrect: true), + SentenceData(text: "I think 두팔 who is my boyfriend should study before wathcing….", isCorrect: true), + SentenceData(text: "but Avatar2 is amazing movie I think.", isCorrect: true), + SentenceData(text: "In my personal opinion", isCorrect: true) + ] + } +} + +@available(iOS 17, *) +#Preview { + DetailDiaryCoachedView() +} From 7d0d63df8d476b985069acec32eb0e8903f4ad5b Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Thu, 21 Nov 2024 14:04:32 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[Chore]=20#222=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네비게이션바 분기 - style 메서드화 --- .../UIComponents/SwiftUINavigationView.swift | 17 +++- .../DetailDiary/DetailDiaryCoachedView.swift | 99 +++++++++++++------ 2 files changed, 83 insertions(+), 33 deletions(-) diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift index 0c80a744..56de734a 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift @@ -7,7 +7,14 @@ import SwiftUI +enum NavigationbarType { + case coachingCompleted + case diaryDetails +} + struct SwiftUINavigationView: View { + let navigationbarType: NavigationbarType + @State private var selectedIndex = 0 let options = ["코칭 OFF", "코칭 ON"] @@ -20,9 +27,11 @@ struct SwiftUINavigationView: View { }) .padding(.leading, 10) -// CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) -// .frame(height: 32) -// .padding(65) + if navigationbarType == .diaryDetails { + CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) + .frame(height: 32) + .padding(65) + } Spacer() @@ -38,5 +47,5 @@ struct SwiftUINavigationView: View { } #Preview { - SwiftUINavigationView() + SwiftUINavigationView(navigationbarType: .diaryDetails) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index cc8a8631..6c5c62f4 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -14,22 +14,84 @@ struct SentenceData: Identifiable { } struct DetailDiaryCoachedView: View { + + @Binding var diaryText: String + @Binding var coachingResponse: CoachingsResponse + @State var currentIndex = 0 + var attributedText: AttributedString { + generateAttributedText(sentences: MockData.sentences) + } + + var body: some View { + SwiftUINavigationView(navigationbarType: .diaryDetails) + + VStack(spacing: screenWidth * (16/screenWidth)) { + ScrollView { + Text(attributedText) + .frame(maxWidth: .infinity, alignment: .leading) + } + .frame(height: screenHeight * (314/screenHeight)) + + Spacer() + } + .padding(.horizontal, screenWidth * (16/screenWidth)) + + HStack { + Spacer() + VStack(alignment: .trailing) { + Text("2023년 3월 27일 4:18PM") + HStack { + Text("유진이") + } + } + .font(Font(UIFont.c3)) + .foregroundColor(Color(UIColor.gray400)) + } + .padding(.horizontal, screenWidth * (16 / screenWidth)) + + VStack(spacing: 20) { + 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 * (326/screenHeight), alignment: .top) + .tabViewStyle(.page(indexDisplayMode: .never)) + + PageControl(currentPage: $currentIndex, + coachingResponse: $coachingResponse) + } + } +} + +extension DetailDiaryCoachedView { + private func generateAttributedText(sentences: [SentenceData]) -> AttributedString { var result = AttributedString("") - for (index, sentence) in MockData.sentences.enumerated() { + for (index, sentence) in sentences.enumerated() { var attributedSentence = AttributedString(sentence.text) // 개별 문장 스타일 지정 attributedSentence.foregroundColor = sentence.isCorrect ? UIColor.gray400 : UIColor.smeemWhite attributedSentence.backgroundColor = sentence.isCorrect ? nil : UIColor.point - attributedSentence.font = .custom("Pretendard", size: 16) + attributedSentence.font = Font(UIFont.b4) // 문장 추가 result += attributedSentence // 마지막 문장이 아니면 공백 추가 - if index < MockData.sentences.count - 1 { + if index < sentences.count - 1 { result += AttributedString(" ") } } @@ -37,36 +99,12 @@ struct DetailDiaryCoachedView: View { return result } - var body: some View { - SwiftUINavigationView() - - VStack(spacing: 16) { - Text(MockData.headerText) - .font(Font.custom("Pretendard", size: 16)) - .foregroundColor(Color(UIColor.smeemBlack)) - .frame(maxWidth: .infinity, alignment: .leading) - - ScrollView { - Text(attributedText) - .frame(maxWidth: .infinity, alignment: .leading) - } - .frame(height: screenHeight * 0.32) - - Spacer() - } - .padding(.horizontal, screenWidth * 0.048) - } -} - -extension DetailDiaryCoachedView { struct MockData { - static let headerText = "일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?" - static let sentences: [SentenceData] = [ SentenceData(text: "I watched Avatar with my boyfriend at Hongdae CGV.", isCorrect: true), SentenceData(text: "I should have skimmed the previous season what they were saying and the universe(??).", isCorrect: false), SentenceData(text: "What I was annoyed then was 두팔 didn't know that as me.", isCorrect: true), - SentenceData(text: "I think 두팔 who is my boyfriend should study before wathcing….", isCorrect: true), + SentenceData(text: "I think 두팔 who is my boyfriend should study before watching….", isCorrect: true), SentenceData(text: "but Avatar2 is amazing movie I think.", isCorrect: true), SentenceData(text: "In my personal opinion", isCorrect: true) ] @@ -75,5 +113,8 @@ extension DetailDiaryCoachedView { @available(iOS 17, *) #Preview { - DetailDiaryCoachedView() + @State var diaryText = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season 
what they were saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character " + @State var coachingResponse = CoachingsResponse.empty + + DetailDiaryCoachedView(diaryText: $diaryText, coachingResponse: $coachingResponse) } From 9a424250f3e471ff3e90dbb080e1e766d0e055ac Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Thu, 21 Nov 2024 18:45:01 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[Feat]=20#222=20-=20Coaching=20On=20Off=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UIComponents/SwiftUINavigationView.swift | 20 +++-- .../DetailDiary/DetailDiaryCoachedView.swift | 77 ++++++++++--------- 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift index 56de734a..563d0ded 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift @@ -14,14 +14,15 @@ enum NavigationbarType { struct SwiftUINavigationView: View { let navigationbarType: NavigationbarType + @Binding var selectedIndex: Int - @State private var selectedIndex = 0 let options = ["코칭 OFF", "코칭 ON"] var body: some View { - HStack() { - Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, - label: { + HStack { + Button(action: { + // 뒤로가기 액션 + }, label: { Image("icnBack") .imageScale(.large) }) @@ -35,17 +36,20 @@ struct SwiftUINavigationView: View { Spacer() - Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, - label: { + Button(action: { + // 닫기 버튼 액션 + }, label: { Text("닫기") .tint(.black) }) .padding(.trailing, 18) } - .frame(height: 66) + .frame(height: screenHeight * (66 / screenHeight)) } } #Preview { - SwiftUINavigationView(navigationbarType: .diaryDetails) + @State var defaultIndex = 0 + + SwiftUINavigationView(navigationbarType: .diaryDetails, selectedIndex: $defaultIndex) } diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index 6c5c62f4..9d00587f 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -18,59 +18,66 @@ struct DetailDiaryCoachedView: View { @Binding var diaryText: String @Binding var coachingResponse: CoachingsResponse @State var currentIndex = 0 + @State private var selectedIndex = 0 var attributedText: AttributedString { generateAttributedText(sentences: MockData.sentences) } var body: some View { - SwiftUINavigationView(navigationbarType: .diaryDetails) - - VStack(spacing: screenWidth * (16/screenWidth)) { + VStack(spacing: screenWidth * (16 / screenWidth)) { + // 네비게이션 바 포함 + SwiftUINavigationView(navigationbarType: .diaryDetails, selectedIndex: $selectedIndex) + + // 본문 내용 ScrollView { Text(attributedText) .frame(maxWidth: .infinity, alignment: .leading) } - .frame(height: screenHeight * (314/screenHeight)) + .frame(height: screenHeight * (314 / screenHeight)) + .padding(.horizontal, screenWidth * (16 / screenWidth)) - Spacer() - } - .padding(.horizontal, screenWidth * (16/screenWidth)) - - HStack { - Spacer() - VStack(alignment: .trailing) { - Text("2023년 3월 27일 4:18PM") - HStack { - Text("유진이") + HStack { + Spacer() + VStack(alignment: .trailing) { + Text("2023년 3월 27일 4:18PM") + HStack { + Text("유진이") + } } + .font(Font(UIFont.c3)) + .foregroundColor(Color(UIColor.gray400)) } - .font(Font(UIFont.c3)) - .foregroundColor(Color(UIColor.gray400)) - } - .padding(.horizontal, screenWidth * (16 / screenWidth)) - - VStack(spacing: 20) { - Rectangle() - .frame(height: screenHeight * (8/screenHeight)) - .foregroundStyle(Color(UIColor.gray100)) + .padding(.horizontal, screenWidth * (16 / screenWidth)) - 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]) + // "코칭 ON"일 때만 표시 + if selectedIndex == 1 { // "코칭 ON"이 선택된 경우 + 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) } + } else { + Spacer(minLength: screenHeight * (342/screenHeight)) } - .frame(width: screenWidth, height: screenHeight * (326/screenHeight), alignment: .top) - .tabViewStyle(.page(indexDisplayMode: .never)) - - PageControl(currentPage: $currentIndex, - coachingResponse: $coachingResponse) } } } From 8b6cabe573941610a8cf29ccef290e7edca8de26 Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Thu, 21 Nov 2024 19:53:28 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[Feat]=20#222=20-=20=EB=B0=94=ED=85=80?= =?UTF-8?q?=EC=8B=9C=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=ED=99=94=20=EB=B0=8F=20index=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 4 + .../UIComponents/CoachingContentView.swift | 45 +++++++++++ .../API/Coaching/CoachingResponse.swift | 28 ++++++- .../DetailDiary/DetailDiaryCoachedView.swift | 80 ++++++------------- 4 files changed, 97 insertions(+), 60 deletions(-) create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingContentView.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 734ad0e4..02b8eec6 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 37BB09BB2CBF8BA800A3B28A /* CoachingCompletedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */; }; 37BCADF22BC3FFBF006EF960 /* DetailDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */; }; 37BDC9872C4FEA940075F68A /* SendFeedbackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BDC9862C4FEA940075F68A /* SendFeedbackView.swift */; }; + 37CA11192CEF32F5006341D3 /* CoachingContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */; }; 37DCA6572A47574300FF8F90 /* RandomTopicAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6562A47574300FF8F90 /* RandomTopicAPI.swift */; }; 37DCA65A2A47598700FF8F90 /* RandomTopicService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6592A47598700FF8F90 /* RandomTopicService.swift */; }; 37DCA65D2A475B5100FF8F90 /* RandomTopicResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA65C2A475B5100FF8F90 /* RandomTopicResponse.swift */; }; @@ -319,6 +320,7 @@ 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingCompletedView.swift; sourceTree = ""; }; 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryViewModel.swift; sourceTree = ""; }; 37BDC9862C4FEA940075F68A /* SendFeedbackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFeedbackView.swift; sourceTree = ""; }; + 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingContentView.swift; sourceTree = ""; }; 37DCA6562A47574300FF8F90 /* RandomTopicAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicAPI.swift; sourceTree = ""; }; 37DCA6592A47598700FF8F90 /* RandomTopicService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicService.swift; sourceTree = ""; }; 37DCA65C2A475B5100FF8F90 /* RandomTopicResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicResponse.swift; sourceTree = ""; }; @@ -697,6 +699,7 @@ 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */, 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */, 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */, + 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */, ); path = UIComponents; sourceTree = ""; @@ -2036,6 +2039,7 @@ 4ABCBCCC2BDE8E44003138A8 /* MySummaryService.swift in Sources */, 371107DA2AC99099007A4AC2 /* KeyboardFollowingLayoutHandler.swift in Sources */, 37B123B62CEEE58E00ED973A /* DetailDiaryCoachedView.swift in Sources */, + 37CA11192CEF32F5006341D3 /* CoachingContentView.swift in Sources */, 4A9FAB452A4C3D1200C40D5A /* BadgeListViewController.swift in Sources */, 4ABCBCE52BE25D82003138A8 /* TrainingPlanViewController.swift in Sources */, 4AB7C9192B75F9B500845733 /* GesturePublisher+.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingContentView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingContentView.swift new file mode 100644 index 00000000..ca8d4667 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingContentView.swift @@ -0,0 +1,45 @@ +// +// CoachingContentView.swift +// Smeem-iOS +// +// Created by Joon Baek on 11/21/24. +// + +import SwiftUI + +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) +} diff --git a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift index 88197c48..b455c245 100644 --- a/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift +++ b/Smeem-iOS/Smeem-iOS/Network/API/Coaching/CoachingResponse.swift @@ -17,9 +17,31 @@ extension CoachingsResponse { reason: "현재완료 시제인 have went는 과거 시제인 went로 바꾸는 것이 맞습니다. yesterday와 함께 사용할 때는 단순 과거 시제를 사용해야 합니다.", is_corrected: true), CoachingResponse(original_sentence: "I have went to the park yesterdayI have went to the park yesterdayI have went럼뉴름ㄴ람ㄴㄹ 마넝롬나ㅣㅓㅇㄹ ㅗㅁ나ㅓㅇ롬나어롬나어롬나러ㅗㅁ나러 ㅗㄴ마러ㅗㅁ너ㅏ롬 ㄴ라ㅓ ㅗㄴㅁ라 왜 갑자 I have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterdayI have went to the park yesterday", - corrected_sentence: "I went to the park yesterdayI went to the park yesterdayI went to the park yesterday", - reason: "이러 이러한 이유로 이건 맞습니다", - is_corrected: true)]) + corrected_sentence: "I went to the park yesterdayI went to the park yesterdayI went to the park yesterday", + reason: "이러 이러한 이유로 이건 맞습니다", + is_corrected: true)]) + static let sample = CoachingsResponse( + corrections: [ + CoachingResponse( + original_sentence: "I should have skimmed the previous season - Avatar1..", + corrected_sentence: "I should have skimmed the previous season - Avatar 1.", + reason: "Avatar1은 'Avatar 1'으로 띄어쓰기해야 하며, 중복된 마침표 하나를 제거해야 합니다.", + is_corrected: true + ), + CoachingResponse( + original_sentence: "I think 두팔 who is my boyfriend should study before wathcing….", + corrected_sentence: "I think 두팔, who is my boyfriend, should study before watching.", + reason: "'wathcing'을 'watching'으로 수정하고, 불필요한 점을 제거했습니다. 또한 쉼표를 추가하여 문장을 읽기 쉽게 만들었습니다.", + is_corrected: true + ), + CoachingResponse( + original_sentence: "In my personal opinion, the jjin main character of Avatar2 is not Sully, but his son.", + corrected_sentence: "In my personal opinion, the real main character of Avatar 2 is not Sully, but his son.", + reason: "'jjin'은 'real'로 대체하였고, 'Avatar2'를 'Avatar 2'로 띄어쓰기 처리하였습니다.", + is_corrected: true + ) + ] + ) } struct CoachingResponse: Codable { diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index 9d00587f..b9d35128 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -21,12 +21,12 @@ struct DetailDiaryCoachedView: View { @State private var selectedIndex = 0 var attributedText: AttributedString { - generateAttributedText(sentences: MockData.sentences) + generateAttributedText() } var body: some View { VStack(spacing: screenWidth * (16 / screenWidth)) { - // 네비게이션 바 포함 + // 네비게이션 바 SwiftUINavigationView(navigationbarType: .diaryDetails, selectedIndex: $selectedIndex) // 본문 내용 @@ -36,6 +36,7 @@ struct DetailDiaryCoachedView: View { } .frame(height: screenHeight * (314 / screenHeight)) .padding(.horizontal, screenWidth * (16 / screenWidth)) + .foregroundColor(Color(UIColor.gray400)) HStack { Spacer() @@ -51,29 +52,9 @@ struct DetailDiaryCoachedView: View { .padding(.horizontal, screenWidth * (16 / screenWidth)) // "코칭 ON"일 때만 표시 - if selectedIndex == 1 { // "코칭 ON"이 선택된 경우 - 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) + if selectedIndex == 1 { + VStack { + CoachingContentView(currentIndex: $currentIndex, coachingResponse: $coachingResponse) } } else { Spacer(minLength: screenHeight * (342/screenHeight)) @@ -83,45 +64,30 @@ struct DetailDiaryCoachedView: View { } extension DetailDiaryCoachedView { - private func generateAttributedText(sentences: [SentenceData]) -> AttributedString { - var result = AttributedString("") - - for (index, sentence) in sentences.enumerated() { - var attributedSentence = AttributedString(sentence.text) - - // 개별 문장 스타일 지정 - attributedSentence.foregroundColor = sentence.isCorrect ? UIColor.gray400 : UIColor.smeemWhite - attributedSentence.backgroundColor = sentence.isCorrect ? nil : UIColor.point - attributedSentence.font = Font(UIFont.b4) - - // 문장 추가 - result += attributedSentence - - // 마지막 문장이 아니면 공백 추가 - if index < sentences.count - 1 { - result += AttributedString(" ") + func generateAttributedText() -> AttributedString { + var attributedText = AttributedString(diaryText) + let corrections = coachingResponse.corrections + + // 강조 조건을 하나의 변수로 묶기 + let shouldHighlightIndex = selectedIndex != 0 ? currentIndex : -1 + + for (index, correction) in corrections.enumerated() { + if index == shouldHighlightIndex { + if let range = attributedText.range(of: correction.original_sentence) { + attributedText[range].backgroundColor = Color(UIColor.point) + attributedText[range].foregroundColor = Color(UIColor.smeemWhite) + } } } - - return result - } - - struct MockData { - static let sentences: [SentenceData] = [ - SentenceData(text: "I watched Avatar with my boyfriend at Hongdae CGV.", isCorrect: true), - SentenceData(text: "I should have skimmed the previous season what they were saying and the universe(??).", isCorrect: false), - SentenceData(text: "What I was annoyed then was 두팔 didn't know that as me.", isCorrect: true), - SentenceData(text: "I think 두팔 who is my boyfriend should study before watching….", isCorrect: true), - SentenceData(text: "but Avatar2 is amazing movie I think.", isCorrect: true), - SentenceData(text: "In my personal opinion", isCorrect: true) - ] + return attributedText } } @available(iOS 17, *) #Preview { - @State var diaryText = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season 
what they were saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character " - @State var coachingResponse = CoachingsResponse.empty + @State var diaryText = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season - Avatar1.. I really couldn’t get what they weere saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character of Avatar2 is not Sully, but his son." + + @State var coachingResponse = CoachingsResponse.sample DetailDiaryCoachedView(diaryText: $diaryText, coachingResponse: $coachingResponse) } From 2296fa135e5282ef5736adec6a9d1f236d2fd875 Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Tue, 26 Nov 2024 22:57:59 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[Feat]=20#222=20-=20FloatingButtonsSwiftUIV?= =?UTF-8?q?iew=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 4 + .../FloatingButtonsSwiftUIView.swift | 82 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/FloatingButtonsSwiftUIView.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 02b8eec6..898814e8 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 375B628B2C590D0D00DA8E30 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 375B628A2C590D0D00DA8E30 /* FirebaseMessaging */; }; 375B628D2C590D0D00DA8E30 /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 375B628C2C590D0D00DA8E30 /* FirebaseRemoteConfig */; }; 3761116C2A278D0E0095EC5A /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761116B2A278D0E0095EC5A /* String+.swift */; }; + 377B3BEE2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */; }; 3785072F2BD1480C004CC922 /* SharedDiaryDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37499E3C2BD0C3A100BA6FAF /* SharedDiaryDataService.swift */; }; 378B20EE2BA0A01600604935 /* ForeignDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B20ED2BA0A01600604935 /* ForeignDiaryViewModel.swift */; }; 378B20F02BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B20EF2BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift */; }; @@ -288,6 +289,7 @@ 374F828D2AC5DFFE00C128B9 /* DiaryBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryBottomView.swift; sourceTree = ""; }; 374FAF802A2CACCF00237A1A /* DiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryViewController.swift; sourceTree = ""; }; 3761116B2A278D0E0095EC5A /* String+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; + 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingButtonsSwiftUIView.swift; sourceTree = ""; }; 378B20ED2BA0A01600604935 /* ForeignDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignDiaryViewModel.swift; sourceTree = ""; }; 378B20EF2BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepOneKoreanDiaryViewModel.swift; sourceTree = ""; }; 378B20F12BA0A03000604935 /* StepTwoKoreanDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepTwoKoreanDiaryViewModel.swift; sourceTree = ""; }; @@ -700,6 +702,7 @@ 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */, 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */, 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */, + 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */, ); path = UIComponents; sourceTree = ""; @@ -2152,6 +2155,7 @@ 4AB7C9322B78B45300845733 /* LoginViewModel.swift in Sources */, 37A574CA29FF980F00312453 /* UIStackView+.swift in Sources */, 6F294A422A26178C00856CC8 /* UserNickNameViewController.swift in Sources */, + 377B3BEE2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift in Sources */, 4A4050EC2CE8C54400E84EDE /* CoachingExplanationView.swift in Sources */, 4AC047252A8FCAE100EBDC0E /* AuthManagementViewController.swift in Sources */, 374FAF812A2CACCF00237A1A /* DiaryViewController.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/FloatingButtonsSwiftUIView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/FloatingButtonsSwiftUIView.swift new file mode 100644 index 00000000..c2290fa4 --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/FloatingButtonsSwiftUIView.swift @@ -0,0 +1,82 @@ +// +// FloatingButtonsSwiftUIView.swift +// Smeem-iOS +// +// Created by Joon Baek on 11/26/24. +// + +import SwiftUI + +struct FloatingButtonsSwiftUIView: View { + @Environment(\.dismiss) var dismiss + @State private var showAlert = false + + var body: some View { + ZStack { + Color.black.opacity(0.3) + .ignoresSafeArea() + .onTapGesture { + dismiss() + } + + VStack { + Spacer() + + VStack(spacing: 0) { + Button(action: { + showAlert = true + }) { + Text("수정하기") + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + .foregroundColor(.blue) + } + .alert("수정시 모든 코칭 내용이 사라집니다. 그래도 수정하시겠습니까?", + isPresented: $showAlert) { + Button("취소", role: .cancel) { dismiss() } + Button("확인", role: .destructive) { + print("수정 확인 버튼 액션") + } + } + + Divider() + .frame(height: 1) + .background(Color.gray.opacity(0.5)) + + Button(action: { + print("삭제하기 버튼 액션") + }) { + Text("삭제하기") + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + .foregroundColor(.red) + } + } + .background(Color.white) + .cornerRadius(14) + + Button(action: { + dismiss() + }) { + Text("취소") + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + .background(.white) + .foregroundColor(.blue) + .cornerRadius(14) + } + .padding(.top, 10) + + Spacer().frame(height: 20) + } + .padding(.horizontal, 20) + } + } +} + +#Preview { + FloatingButtonsSwiftUIView() +} From b4a39cbff76fb848d65ad5eff49517dcca592299 Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Tue, 26 Nov 2024 23:42:42 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[Feat]=20#222=20-=20Highlight=20Modifier=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 4 ++ .../Smeem-iOS/Global/HighlightModifier.swift | 45 +++++++++++++++++++ .../DetailDiary/DetailDiaryCoachedView.swift | 43 ++++-------------- 3 files changed, 58 insertions(+), 34 deletions(-) create mode 100644 Smeem-iOS/Smeem-iOS/Global/HighlightModifier.swift diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 898814e8..3805e1a1 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 375B628D2C590D0D00DA8E30 /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 375B628C2C590D0D00DA8E30 /* FirebaseRemoteConfig */; }; 3761116C2A278D0E0095EC5A /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761116B2A278D0E0095EC5A /* String+.swift */; }; 377B3BEE2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */; }; + 377B3BF02CF613710086E0BC /* HighlightModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377B3BEF2CF613710086E0BC /* HighlightModifier.swift */; }; 3785072F2BD1480C004CC922 /* SharedDiaryDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37499E3C2BD0C3A100BA6FAF /* SharedDiaryDataService.swift */; }; 378B20EE2BA0A01600604935 /* ForeignDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B20ED2BA0A01600604935 /* ForeignDiaryViewModel.swift */; }; 378B20F02BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B20EF2BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift */; }; @@ -290,6 +291,7 @@ 374FAF802A2CACCF00237A1A /* DiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryViewController.swift; sourceTree = ""; }; 3761116B2A278D0E0095EC5A /* String+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingButtonsSwiftUIView.swift; sourceTree = ""; }; + 377B3BEF2CF613710086E0BC /* HighlightModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightModifier.swift; sourceTree = ""; }; 378B20ED2BA0A01600604935 /* ForeignDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignDiaryViewModel.swift; sourceTree = ""; }; 378B20EF2BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepOneKoreanDiaryViewModel.swift; sourceTree = ""; }; 378B20F12BA0A03000604935 /* StepTwoKoreanDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepTwoKoreanDiaryViewModel.swift; sourceTree = ""; }; @@ -635,6 +637,7 @@ 37A574B929FE209F00312453 /* Resources */, 4AC4B82B2A2F9F5300E147AA /* Constants */, 37EB69E42A4B3B6B00075E4E /* Protocols */, + 377B3BEF2CF613710086E0BC /* HighlightModifier.swift */, ); path = Global; sourceTree = ""; @@ -2053,6 +2056,7 @@ 4A9E10E42A43468600295D07 /* BaseTargetType.swift in Sources */, 4A4FEB092B728144001BBDF3 /* TrainingWayViewModel.swift in Sources */, 37A574C329FF6E9C00312453 /* Constant.swift in Sources */, + 377B3BF02CF613710086E0BC /* HighlightModifier.swift in Sources */, 4AC1D6832B4ED38200D39A98 /* OnboardingModel.swift in Sources */, 4AC00EEB2AD4527A009230DA /* TrainingWayViewController.swift in Sources */, 4AC705932BEA1E52003C5310 /* LanguageContainerView.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/HighlightModifier.swift b/Smeem-iOS/Smeem-iOS/Global/HighlightModifier.swift new file mode 100644 index 00000000..f34727fe --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/HighlightModifier.swift @@ -0,0 +1,45 @@ +// +// HighlightModifier.swift +// Smeem-iOS +// +// Created by Joon Baek on 11/26/24. +// + +import SwiftUI + +/* +# 사용법 + - `diaryText`: 강조 표시를 적용할 전체 텍스트 (String) + - `corrections`: 강조할 문장의 목록 (CoachingResponse 배열) + - `highlightIndex`: 강조할 문장의 인덱스 (Int) + + `Text` 뷰에 `.modifier(HighlightModifier(...))`로 적용 + +# 예제 코드 + +Text(diaryText) + .modifier(HighlightModifier( + diaryText: diaryText, + corrections: coachingResponse.corrections, + highlightIndex: currentIndex + )) +*/ + +struct HighlightModifier: ViewModifier { + let diaryText: String + let corrections: [CoachingResponse] + let highlightIndex: Int + + func body(content: Content) -> some View { + var attributedText = AttributedString(diaryText) + + for (index, correction) in corrections.enumerated() { + if index == highlightIndex, let range = attributedText.range(of: correction.original_sentence) { + attributedText[range].backgroundColor = Color(UIColor.point) + attributedText[range].foregroundColor = Color(UIColor.smeemWhite) + } + } + + return Text(attributedText) + } +} diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index b9d35128..e46636cc 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -7,12 +7,6 @@ import SwiftUI -struct SentenceData: Identifiable { - let id = UUID() - let text: String - let isCorrect: Bool -} - struct DetailDiaryCoachedView: View { @Binding var diaryText: String @@ -20,10 +14,6 @@ struct DetailDiaryCoachedView: View { @State var currentIndex = 0 @State private var selectedIndex = 0 - var attributedText: AttributedString { - generateAttributedText() - } - var body: some View { VStack(spacing: screenWidth * (16 / screenWidth)) { // 네비게이션 바 @@ -31,7 +21,12 @@ struct DetailDiaryCoachedView: View { // 본문 내용 ScrollView { - Text(attributedText) + Text(diaryText) + .modifier(HighlightModifier( + diaryText: diaryText, + corrections: coachingResponse.corrections, + highlightIndex: selectedIndex != 0 ? currentIndex : -1 + )) .frame(maxWidth: .infinity, alignment: .leading) } .frame(height: screenHeight * (314 / screenHeight)) @@ -54,39 +49,19 @@ struct DetailDiaryCoachedView: View { // "코칭 ON"일 때만 표시 if selectedIndex == 1 { VStack { - CoachingContentView(currentIndex: $currentIndex, coachingResponse: $coachingResponse) + CoachingContentView(currentIndex: $currentIndex, coachingResponse: $coachingResponse) } } else { - Spacer(minLength: screenHeight * (342/screenHeight)) - } - } - } -} - -extension DetailDiaryCoachedView { - func generateAttributedText() -> AttributedString { - var attributedText = AttributedString(diaryText) - let corrections = coachingResponse.corrections - - // 강조 조건을 하나의 변수로 묶기 - let shouldHighlightIndex = selectedIndex != 0 ? currentIndex : -1 - - for (index, correction) in corrections.enumerated() { - if index == shouldHighlightIndex { - if let range = attributedText.range(of: correction.original_sentence) { - attributedText[range].backgroundColor = Color(UIColor.point) - attributedText[range].foregroundColor = Color(UIColor.smeemWhite) - } + Spacer(minLength: screenHeight * (342 / screenHeight)) } } - return attributedText } } @available(iOS 17, *) #Preview { @State var diaryText = "I watched Avatar with my boyfriend at Hongdae CGV. I should have skimmed the previous season - Avatar1.. I really couldn’t get what they weere saying and the universe(??). What I was annoyed then was 두팔 didn’t know that as me. I think 두팔 who is my boyfriend should study before wathcing…. but Avatar2 is amazing movie I think. In my personal opinion, the jjin main character of Avatar2 is not Sully, but his son." - + @State var coachingResponse = CoachingsResponse.sample DetailDiaryCoachedView(diaryText: $diaryText, coachingResponse: $coachingResponse) From d7bbf83cd9b2248b6214a92bbdd41ad1581a35c1 Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Wed, 27 Nov 2024 00:04:46 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[Chore]=20#222=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ScrollableView 컴포넌트화 --- Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj | 31 ++---------- .../CoachingComparisonView.swift | 0 .../{ => SwiftUI}/CoachingCompletedView.swift | 0 .../{ => SwiftUI}/CoachingContentView.swift | 0 .../CoachingExplanationView.swift | 0 .../CustomSegmentedControl.swift | 0 .../FloatingButtonsSwiftUIView.swift | 0 .../SwiftUI/ScrollableDiaryView.swift | 50 +++++++++++++++++++ .../{ => SwiftUI}/SwiftUINavigationView.swift | 0 .../DetailDiary/DetailDiaryCoachedView.swift | 43 ++++++---------- 10 files changed, 67 insertions(+), 57 deletions(-) rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/CoachingComparisonView.swift (100%) rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/CoachingCompletedView.swift (100%) rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/CoachingContentView.swift (100%) rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/CoachingExplanationView.swift (100%) rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/CustomSegmentedControl.swift (100%) rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/FloatingButtonsSwiftUIView.swift (100%) create mode 100644 Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift rename Smeem-iOS/Smeem-iOS/Global/UIComponents/{ => SwiftUI}/SwiftUINavigationView.swift (100%) diff --git a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj index 3805e1a1..4a495040 100644 --- a/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj +++ b/Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj @@ -26,8 +26,6 @@ 3728B8D72A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3728B8D62A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift */; }; 373A7F552C3D78CC00CF554C /* CustomBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */; }; 373D29932CBFE17B00A559A3 /* View+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29922CBFE17B00A559A3 /* View+.swift */; }; - 373D29962CC2A84E00A559A3 /* SwiftUINavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */; }; - 373D29982CC7A53C00A559A3 /* CustomSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */; }; 373E38592A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373E38582A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift */; }; 374F828C2AC327A200C128B9 /* SmeemTextViewHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374F828B2AC327A200C128B9 /* SmeemTextViewHandler.swift */; }; 374F828E2AC5DFFE00C128B9 /* DiaryBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374F828D2AC5DFFE00C128B9 /* DiaryBottomView.swift */; }; @@ -37,7 +35,6 @@ 375B628B2C590D0D00DA8E30 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 375B628A2C590D0D00DA8E30 /* FirebaseMessaging */; }; 375B628D2C590D0D00DA8E30 /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 375B628C2C590D0D00DA8E30 /* FirebaseRemoteConfig */; }; 3761116C2A278D0E0095EC5A /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761116B2A278D0E0095EC5A /* String+.swift */; }; - 377B3BEE2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */; }; 377B3BF02CF613710086E0BC /* HighlightModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377B3BEF2CF613710086E0BC /* HighlightModifier.swift */; }; 3785072F2BD1480C004CC922 /* SharedDiaryDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37499E3C2BD0C3A100BA6FAF /* SharedDiaryDataService.swift */; }; 378B20EE2BA0A01600604935 /* ForeignDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B20ED2BA0A01600604935 /* ForeignDiaryViewModel.swift */; }; @@ -69,10 +66,8 @@ 37ADCC022B0289AD00E474AA /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37ADCC012B0289AD00E474AA /* Observable.swift */; }; 37B123B62CEEE58E00ED973A /* DetailDiaryCoachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B123B52CEEE58E00ED973A /* DetailDiaryCoachedView.swift */; }; 37B360AA2AC2A4680006C8ED /* SmeemTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B360A92AC2A4680006C8ED /* SmeemTextView.swift */; }; - 37BB09BB2CBF8BA800A3B28A /* CoachingCompletedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */; }; 37BCADF22BC3FFBF006EF960 /* DetailDiaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */; }; 37BDC9872C4FEA940075F68A /* SendFeedbackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BDC9862C4FEA940075F68A /* SendFeedbackView.swift */; }; - 37CA11192CEF32F5006341D3 /* CoachingContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */; }; 37DCA6572A47574300FF8F90 /* RandomTopicAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6562A47574300FF8F90 /* RandomTopicAPI.swift */; }; 37DCA65A2A47598700FF8F90 /* RandomTopicService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6592A47598700FF8F90 /* RandomTopicService.swift */; }; 37DCA65D2A475B5100FF8F90 /* RandomTopicResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA65C2A475B5100FF8F90 /* RandomTopicResponse.swift */; }; @@ -108,7 +103,6 @@ 4A0EE6042B9301E900158899 /* OnboardingServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A0EE6032B9301E900158899 /* OnboardingServiceProtocol.swift */; }; 4A10A3A82AB8AA5800C145E9 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A10A3A72AB8AA5800C145E9 /* System.swift */; }; 4A1882FA2A8BBD0D0088F590 /* SmeemStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD923E62A0650B600FF5E27 /* SmeemStartViewController.swift */; }; - 4A1F2AE72CD4A1740055587F /* CoachingComparisonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */; }; 4A2AEE702C3D78E70000CBCB /* ResignSummaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2AEE6F2C3D78E70000CBCB /* ResignSummaryViewModel.swift */; }; 4A3373F52A45BF6600EFE6C4 /* OnboardingEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3373F42A45BF6600EFE6C4 /* OnboardingEndPoint.swift */; }; 4A3373F82A461ABC00EFE6C4 /* OnboardingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3373F72A461ABC00EFE6C4 /* OnboardingService.swift */; }; @@ -116,7 +110,6 @@ 4A3F2E972A0ABBAA00F6AC60 /* AlarmCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3F2E962A0ABBAA00F6AC60 /* AlarmCollectionViewCell.swift */; }; 4A3F2E9C2A0B4C7100F6AC60 /* AlarmCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3F2E9B2A0B4C7100F6AC60 /* AlarmCollectionView.swift */; }; 4A3F2EA02A0C18B600F6AC60 /* DatePickerFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3F2E9F2A0C18B600F6AC60 /* DatePickerFooterView.swift */; }; - 4A4050EC2CE8C54400E84EDE /* CoachingExplanationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */; }; 4A454A832B6666FF0079D48D /* TrainingGoalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A454A822B6666FF0079D48D /* TrainingGoalViewModel.swift */; }; 4A454A852B6667180079D48D /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A454A842B6667180079D48D /* ViewModel.swift */; }; 4A454A882B691F850079D48D /* EditGoalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A454A872B691F850079D48D /* EditGoalViewController.swift */; }; @@ -282,15 +275,12 @@ 3728B8D62A2CFF3A00EF4CF8 /* StepTwoKoreanDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepTwoKoreanDiaryViewController.swift; sourceTree = ""; }; 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBannerView.swift; sourceTree = ""; }; 373D29922CBFE17B00A559A3 /* View+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+.swift"; sourceTree = ""; }; - 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUINavigationView.swift; sourceTree = ""; }; - 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSegmentedControl.swift; sourceTree = ""; }; 373E38582A2F889500DF96F0 /* DiaryDetailRandomSubjectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryDetailRandomSubjectView.swift; sourceTree = ""; }; 37499E3C2BD0C3A100BA6FAF /* SharedDiaryDataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDiaryDataService.swift; sourceTree = ""; }; 374F828B2AC327A200C128B9 /* SmeemTextViewHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemTextViewHandler.swift; sourceTree = ""; }; 374F828D2AC5DFFE00C128B9 /* DiaryBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryBottomView.swift; sourceTree = ""; }; 374FAF802A2CACCF00237A1A /* DiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryViewController.swift; sourceTree = ""; }; 3761116B2A278D0E0095EC5A /* String+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; - 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingButtonsSwiftUIView.swift; sourceTree = ""; }; 377B3BEF2CF613710086E0BC /* HighlightModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightModifier.swift; sourceTree = ""; }; 378B20ED2BA0A01600604935 /* ForeignDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignDiaryViewModel.swift; sourceTree = ""; }; 378B20EF2BA0A02400604935 /* StepOneKoreanDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepOneKoreanDiaryViewModel.swift; sourceTree = ""; }; @@ -321,10 +311,8 @@ 37ADCC012B0289AD00E474AA /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; 37B123B52CEEE58E00ED973A /* DetailDiaryCoachedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryCoachedView.swift; sourceTree = ""; }; 37B360A92AC2A4680006C8ED /* SmeemTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmeemTextView.swift; sourceTree = ""; }; - 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingCompletedView.swift; sourceTree = ""; }; 37BCADF12BC3FFBF006EF960 /* DetailDiaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryViewModel.swift; sourceTree = ""; }; 37BDC9862C4FEA940075F68A /* SendFeedbackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFeedbackView.swift; sourceTree = ""; }; - 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingContentView.swift; sourceTree = ""; }; 37DCA6562A47574300FF8F90 /* RandomTopicAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicAPI.swift; sourceTree = ""; }; 37DCA6592A47598700FF8F90 /* RandomTopicService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicService.swift; sourceTree = ""; }; 37DCA65C2A475B5100FF8F90 /* RandomTopicResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomTopicResponse.swift; sourceTree = ""; }; @@ -361,7 +349,6 @@ 4A0EE6032B9301E900158899 /* OnboardingServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingServiceProtocol.swift; sourceTree = ""; }; 4A10A3A72AB8AA5800C145E9 /* System.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = ""; }; 4A13B7582A13D37000F4FB1E /* UserNickNameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNickNameViewController.swift; sourceTree = ""; }; - 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingComparisonView.swift; sourceTree = ""; }; 4A23621B2AFA8682005E4C58 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 4A23621C2AFA868D005E4C58 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 4A2AEE6F2C3D78E70000CBCB /* ResignSummaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignSummaryViewModel.swift; sourceTree = ""; }; @@ -371,7 +358,6 @@ 4A3F2E962A0ABBAA00F6AC60 /* AlarmCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmCollectionViewCell.swift; sourceTree = ""; }; 4A3F2E9B2A0B4C7100F6AC60 /* AlarmCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmCollectionView.swift; sourceTree = ""; }; 4A3F2E9F2A0C18B600F6AC60 /* DatePickerFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerFooterView.swift; sourceTree = ""; }; - 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachingExplanationView.swift; sourceTree = ""; }; 4A454A822B6666FF0079D48D /* TrainingGoalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingGoalViewModel.swift; sourceTree = ""; }; 4A454A842B6667180079D48D /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 4A454A872B691F850079D48D /* EditGoalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditGoalViewController.swift; sourceTree = ""; }; @@ -504,6 +490,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + 377B3BF12CF618B90086E0BC /* SwiftUI */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = SwiftUI; sourceTree = ""; }; 4AF050CE2CE9DE010055BC3F /* Coaching */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Coaching; sourceTree = ""; }; 4AF050D72CEA01E20055BC3F /* DiaryComplete */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = DiaryComplete; sourceTree = ""; }; 4AF050D82CEA01EA0055BC3F /* Coaching */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Coaching; sourceTree = ""; }; @@ -692,6 +679,7 @@ 37A574B729FE208A00312453 /* UIComponents */ = { isa = PBXGroup; children = ( + 377B3BF12CF618B90086E0BC /* SwiftUI */, 4A81C2952ACDAB0A0056E815 /* HowLearning */, 4A81C2942ACDAAF80056E815 /* PlanGoal */, 371107DB2ACAB4E3007A4AC2 /* Base */, @@ -699,13 +687,6 @@ 4A1EE9532A4DFB42007BFEF3 /* Diary */, 4A3F2E952A0ABB9C00F6AC60 /* Alarm */, 373A7F542C3D78CC00CF554C /* CustomBannerView.swift */, - 37BB09BA2CBF8BA800A3B28A /* CoachingCompletedView.swift */, - 373D29952CC2A84E00A559A3 /* SwiftUINavigationView.swift */, - 373D29972CC7A53C00A559A3 /* CustomSegmentedControl.swift */, - 4A4050EB2CE8C54400E84EDE /* CoachingExplanationView.swift */, - 4A1F2AE62CD4A15B0055587F /* CoachingComparisonView.swift */, - 37CA11182CEF32F5006341D3 /* CoachingContentView.swift */, - 377B3BED2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift */, ); path = UIComponents; sourceTree = ""; @@ -1870,6 +1851,7 @@ dependencies = ( ); fileSystemSynchronizedGroups = ( + 377B3BF12CF618B90086E0BC /* SwiftUI */, 4AF050CE2CE9DE010055BC3F /* Coaching */, 4AF050D72CEA01E20055BC3F /* DiaryComplete */, 4AF050D82CEA01EA0055BC3F /* Coaching */, @@ -2045,13 +2027,11 @@ 4ABCBCCC2BDE8E44003138A8 /* MySummaryService.swift in Sources */, 371107DA2AC99099007A4AC2 /* KeyboardFollowingLayoutHandler.swift in Sources */, 37B123B62CEEE58E00ED973A /* DetailDiaryCoachedView.swift in Sources */, - 37CA11192CEF32F5006341D3 /* CoachingContentView.swift in Sources */, 4A9FAB452A4C3D1200C40D5A /* BadgeListViewController.swift in Sources */, 4ABCBCE52BE25D82003138A8 /* TrainingPlanViewController.swift in Sources */, 4AB7C9192B75F9B500845733 /* GesturePublisher+.swift in Sources */, 6F294A462A2617D500856CC8 /* TrainingWayView.swift in Sources */, 37A574CC29FF990E00312453 /* UIViewController+.swift in Sources */, - 373D29982CC7A53C00A559A3 /* CustomSegmentedControl.swift in Sources */, 4ABCBCEA2BE26079003138A8 /* TrainingPlanViewModel.swift in Sources */, 4A9E10E42A43468600295D07 /* BaseTargetType.swift in Sources */, 4A4FEB092B728144001BBDF3 /* TrainingWayViewModel.swift in Sources */, @@ -2069,7 +2049,6 @@ 6FE1298C2A498338005536C4 /* HomeService.swift in Sources */, 4AD04B3D2A190E9A004B7A58 /* UITextView+.swift in Sources */, 4ABCBCC72BDD3983003138A8 /* MySummaryServiceProtocol.swift in Sources */, - 4A1F2AE72CD4A1740055587F /* CoachingComparisonView.swift in Sources */, 6F31802F2A24D1DB00089870 /* CalendarCell.swift in Sources */, 6FE1298A2A4982DA005536C4 /* HomeAPI.swift in Sources */, 374F828E2AC5DFFE00C128B9 /* DiaryBottomView.swift in Sources */, @@ -2131,7 +2110,6 @@ 4AFD7C292B600FED00FE7EFF /* NetworkManager.swift in Sources */, 37DCA6652A48052200FF8F90 /* DetailDiaryResponse.swift in Sources */, 4ABCBCD82BE2230F003138A8 /* MyPlanCollectionViewDataSource.swift in Sources */, - 37BB09BB2CBF8BA800A3B28A /* CoachingCompletedView.swift in Sources */, 4AC4B82D2A2F9FE200E147AA /* UserDefaultsManager.swift in Sources */, 37A574E02A00CDCD00312453 /* SeparationLine.swift in Sources */, 4ABCBCDC2BE2484B003138A8 /* MyPlanAppData.swift in Sources */, @@ -2159,8 +2137,6 @@ 4AB7C9322B78B45300845733 /* LoginViewModel.swift in Sources */, 37A574CA29FF980F00312453 /* UIStackView+.swift in Sources */, 6F294A422A26178C00856CC8 /* UserNickNameViewController.swift in Sources */, - 377B3BEE2CF6036C0086E0BC /* FloatingButtonsSwiftUIView.swift in Sources */, - 4A4050EC2CE8C54400E84EDE /* CoachingExplanationView.swift in Sources */, 4AC047252A8FCAE100EBDC0E /* AuthManagementViewController.swift in Sources */, 374FAF812A2CACCF00237A1A /* DiaryViewController.swift in Sources */, 37DCA65A2A47598700FF8F90 /* RandomTopicService.swift in Sources */, @@ -2192,7 +2168,6 @@ 3797891C2AAE11D400C61EF4 /* NavigationBarConfigurationBuilder.swift in Sources */, 4A3373F82A461ABC00EFE6C4 /* OnboardingService.swift in Sources */, 4A9731D72BAAB6E100DEC0C8 /* SplashServiceProtocol.swift in Sources */, - 373D29962CC2A84E00A559A3 /* SwiftUINavigationView.swift in Sources */, 4AA5E4B42BF2355300F308C8 /* MySummaryViewModel2.swift in Sources */, 37A574E82A02424F00312453 /* RandomTopicView.swift in Sources */, 4ABCBCCA2BDD3E3D003138A8 /* MySummaryResponse.swift in Sources */, diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingComparisonView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingComparisonView.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingComparisonView.swift diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingCompletedView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingCompletedView.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingCompletedView.swift diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingContentView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingContentView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingContentView.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingContentView.swift diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingExplanationView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/CoachingExplanationView.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CoachingExplanationView.swift diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CustomSegmentedControl.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/CustomSegmentedControl.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/CustomSegmentedControl.swift diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/FloatingButtonsSwiftUIView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/FloatingButtonsSwiftUIView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/FloatingButtonsSwiftUIView.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/FloatingButtonsSwiftUIView.swift diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift new file mode 100644 index 00000000..5a946cba --- /dev/null +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift @@ -0,0 +1,50 @@ +// +// ScrollableDiaryView.swift +// Smeem-iOS +// +// Created by Joon Baek on 11/26/24. +// + +import SwiftUI + +struct ScrollableDiaryView: View { + let diaryText: String + let corrections: [CoachingResponse] + let currentIndex: Int + let selectedIndex: Int + let screenHeight: CGFloat + let screenWidth: CGFloat + let dateText: String + let authorText: String + + var body: some View { + VStack(spacing: 0) { + // 일기 본문 + ScrollView { + Text(diaryText) + .modifier(HighlightModifier( + diaryText: diaryText, + corrections: corrections, + highlightIndex: selectedIndex != 0 ? currentIndex : -1 + )) + .frame(maxWidth: .infinity, alignment: .leading) + + } + .frame(height: screenHeight * (314 / screenHeight)) + + // 작성 날짜, 작성자 + HStack { + Spacer() + VStack(alignment: .trailing) { + Text(dateText) + HStack { + Text(authorText) + } + } + .font(Font(UIFont.c3)) + } + } + .foregroundColor(Color(UIColor.gray400)) + .padding(.horizontal, screenWidth * (16 / screenWidth)) + } +} diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift similarity index 100% rename from Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUINavigationView.swift rename to Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index e46636cc..74553f77 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -16,40 +16,25 @@ struct DetailDiaryCoachedView: View { var body: some View { VStack(spacing: screenWidth * (16 / screenWidth)) { - // 네비게이션 바 - SwiftUINavigationView(navigationbarType: .diaryDetails, selectedIndex: $selectedIndex) + SwiftUINavigationView(navigationbarType: .diaryDetails, + selectedIndex: $selectedIndex) - // 본문 내용 - ScrollView { - Text(diaryText) - .modifier(HighlightModifier( - diaryText: diaryText, - corrections: coachingResponse.corrections, - highlightIndex: selectedIndex != 0 ? currentIndex : -1 - )) - .frame(maxWidth: .infinity, alignment: .leading) - } - .frame(height: screenHeight * (314 / screenHeight)) - .padding(.horizontal, screenWidth * (16 / screenWidth)) - .foregroundColor(Color(UIColor.gray400)) - - HStack { - Spacer() - VStack(alignment: .trailing) { - Text("2023년 3월 27일 4:18PM") - HStack { - Text("유진이") - } - } - .font(Font(UIFont.c3)) - .foregroundColor(Color(UIColor.gray400)) - } - .padding(.horizontal, screenWidth * (16 / screenWidth)) + ScrollableDiaryView( + diaryText: diaryText, + corrections: coachingResponse.corrections, + currentIndex: currentIndex, + selectedIndex: selectedIndex, + screenHeight: screenHeight, + screenWidth: screenWidth, + dateText: "2023년 3월 27일 4:18PM", + authorText: "유진이" + ) // "코칭 ON"일 때만 표시 if selectedIndex == 1 { VStack { - CoachingContentView(currentIndex: $currentIndex, coachingResponse: $coachingResponse) + CoachingContentView(currentIndex: $currentIndex, + coachingResponse: $coachingResponse) } } else { Spacer(minLength: screenHeight * (342 / screenHeight)) From eadb4bcc22c8ccf5bd21fca26862c2cd025b3e0a Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Wed, 27 Nov 2024 10:30:23 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[Chore]=20#222=20-=20=EB=84=A4=EB=B9=84?= =?UTF-8?q?=EB=B0=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwiftUI/SwiftUINavigationView.swift | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift index 563d0ded..95d30984 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/SwiftUINavigationView.swift @@ -14,35 +14,55 @@ enum NavigationbarType { struct SwiftUINavigationView: View { let navigationbarType: NavigationbarType + @State private var showFloatingView = false @Binding var selectedIndex: Int let options = ["코칭 OFF", "코칭 ON"] var body: some View { HStack { - Button(action: { - // 뒤로가기 액션 - }, label: { - Image("icnBack") - .imageScale(.large) - }) - .padding(.leading, 10) + // 뒤로가기 버튼 + if navigationbarType == .diaryDetails { + Button(action: { + // 뒤로가기 액션 + }, label: { + Image("icnBack") + .imageScale(.large) + }) + .padding(.leading, 10 / screenWidth) + } else { + Spacer().frame(width: 30 / screenWidth) + } + // 중앙 콘텐츠 (CustomSegmentedControl) if navigationbarType == .diaryDetails { CustomSegmentedControl(selectedIndex: $selectedIndex, options: options) - .frame(height: 32) + .frame(height: 32 / screenHeight) .padding(65) } Spacer() - Button(action: { - // 닫기 버튼 액션 - }, label: { - Text("닫기") - .tint(.black) - }) - .padding(.trailing, 18) + // 오른쪽 버튼 + if navigationbarType == .diaryDetails { + Button(action: { + showFloatingView = true + }, label: { + Image("icnMore") + }) + .padding(.trailing, 18 / screenWidth) + .fullScreenCover(isPresented: $showFloatingView) { + FloatingButtonsSwiftUIView() + } + } else { + Button(action: { + // 다른 버튼 액션 + }, label: { + Text("닫기") + .tint(.black) + }) + .padding(.trailing, 18 / screenWidth) + } } .frame(height: screenHeight * (66 / screenHeight)) } From 506a3edbc41293da54fb3bb620c4893ae062830a Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Thu, 28 Nov 2024 16:07:02 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[Chore]=20#222=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwiftUI/ScrollableDiaryView.swift | 60 +++++++++++-------- .../DetailDiary/DetailDiaryCoachedView.swift | 9 +-- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift index 5a946cba..e334c2d3 100644 --- a/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift +++ b/Smeem-iOS/Smeem-iOS/Global/UIComponents/SwiftUI/ScrollableDiaryView.swift @@ -12,39 +12,51 @@ struct ScrollableDiaryView: View { let corrections: [CoachingResponse] let currentIndex: Int let selectedIndex: Int - let screenHeight: CGFloat - let screenWidth: CGFloat let dateText: String let authorText: String - + + @State private var contentHeight: CGFloat = 0 + var body: some View { VStack(spacing: 0) { - // 일기 본문 + // 본문 내용 ScrollView { - Text(diaryText) - .modifier(HighlightModifier( - diaryText: diaryText, - corrections: corrections, - highlightIndex: selectedIndex != 0 ? currentIndex : -1 - )) - .frame(maxWidth: .infinity, alignment: .leading) - - } - .frame(height: screenHeight * (314 / screenHeight)) - - // 작성 날짜, 작성자 - HStack { - Spacer() - VStack(alignment: .trailing) { - Text(dateText) + VStack(alignment: .leading, spacing: screenWidth * 0.02) { + // 텍스트 내용 + Text(diaryText) + .modifier(HighlightModifier( + diaryText: diaryText, + corrections: corrections, + highlightIndex: selectedIndex != 0 ? currentIndex : -1 + )) + .padding(.horizontal, screenHeight * (18 / screenHeight)) + .padding(.bottom, screenHeight * (16 / screenHeight)) + .foregroundColor(Color(UIColor.gray400)) + .background( // 콘텐츠 크기를 측정하기 위한 백그라운드 + GeometryReader { geometry in + Color.clear + .onAppear { + contentHeight = geometry.size.height + } + } + ) + + // Footer (작성 날짜, 작성자) HStack { - Text(authorText) + Spacer() + VStack(alignment: .trailing, spacing: screenWidth * (4 / screenWidth)) { + Text(dateText) + Text(authorText) + } + .font(Font(UIFont.c3)) + .foregroundColor(Color(UIColor.gray400)) } + .padding(.horizontal, screenWidth * (18 / screenWidth)) } - .font(Font(UIFont.c3)) + .padding(.bottom, screenHeight * (16 / screenHeight)) } + .frame(maxHeight: contentHeight + screenHeight * (100 / screenHeight)) // 텍스트 높이에 따른 동적 변경 } - .foregroundColor(Color(UIColor.gray400)) - .padding(.horizontal, screenWidth * (16 / screenWidth)) } } + diff --git a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift index 74553f77..7dca18fb 100644 --- a/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift +++ b/Smeem-iOS/Smeem-iOS/Presentation/DetailDiary/DetailDiaryCoachedView.swift @@ -24,18 +24,15 @@ struct DetailDiaryCoachedView: View { corrections: coachingResponse.corrections, currentIndex: currentIndex, selectedIndex: selectedIndex, - screenHeight: screenHeight, - screenWidth: screenWidth, dateText: "2023년 3월 27일 4:18PM", authorText: "유진이" ) // "코칭 ON"일 때만 표시 if selectedIndex == 1 { - VStack { - CoachingContentView(currentIndex: $currentIndex, - coachingResponse: $coachingResponse) - } + Spacer() + CoachingContentView(currentIndex: $currentIndex, + coachingResponse: $coachingResponse) } else { Spacer(minLength: screenHeight * (342 / screenHeight)) }