From 505f66f636eadf96dc52363bfa775c66ad1f2221 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sat, 18 Nov 2023 17:52:57 +0900 Subject: [PATCH 01/40] =?UTF-8?q?[Feat]=20#194=20-=20=EB=A7=88=EB=9D=BC?= =?UTF-8?q?=ED=86=A4=20Title=20=EB=B6=80=EB=B6=84=20=EA=B8=B0=EB=8A=A5=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 --- .../MarathonTitleCollectionViewCell.swift | 61 +++++++++++++++++++ .../Views/VC/CourseDiscoveryVC.swift | 41 +++++++++---- 2 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift new file mode 100644 index 00000000..4fae3cee --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift @@ -0,0 +1,61 @@ +// +// MarathonTitleCollectionViewCell.swift +// Runnect-iOS +// +// Created by 이명진 on 11/18/23. +// + +import UIKit + +class MarathonTitleCollectionViewCell: UICollectionViewCell { + + // MARK: - UI Components + + private lazy var titleStackView = UIStackView(arrangedSubviews: [mainLabel, subLabel]).then { + $0.axis = .vertical + $0.spacing = 6 + $0.alignment = .leading + } + + private let mainLabel: UILabel = { + let label = UILabel() + label.text = "2023 마라톤 코스" + label.font = UIFont.h3 + label.textColor = UIColor.g1 + return label + }() + private let subLabel: UILabel = { + let label = UILabel() + label.text = "실제 마라톤 코스를 만나보세요" + label.font = UIFont.b6 + label.textColor = UIColor.g2 + return label + }() + + // MARK: - Life cycle + + override init(frame: CGRect) { + super.init(frame: frame) + layout() + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Extensions + +extension MarathonTitleCollectionViewCell { + + // MARK: - Layout Helpers + + func layout() { + contentView.backgroundColor = .clear + contentView.addSubview(titleStackView) + + titleStackView.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.leading.equalToSuperview().inset(16) + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 488528fe..f46b120c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -21,6 +21,8 @@ final class CourseDiscoveryVC: UIViewController { private var courseList = [PublicCourse]() + private var specialList = [String]() + // pagination 에 꼭 필요한 위한 변수들 입니다. private var pageNo = 1 @@ -49,7 +51,6 @@ final class CourseDiscoveryVC: UIViewController { private lazy var mapCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .clear collectionView.isScrollEnabled = true @@ -93,7 +94,10 @@ extension CourseDiscoveryVC { } private func register() { + self.mapCollectionView.register(AdImageCollectionViewCell.self, forCellWithReuseIdentifier: AdImageCollectionViewCell.className) + self.mapCollectionView.register(MarathonTitleCollectionViewCell.self, forCellWithReuseIdentifier: MarathonTitleCollectionViewCell.className) + self.mapCollectionView.register(MarathonMapCollectionViewCell.self, forCellWithReuseIdentifier: MarathonMapCollectionViewCell.className) self.mapCollectionView.register(TitleCollectionViewCell.self, forCellWithReuseIdentifier: TitleCollectionViewCell.className) self.mapCollectionView.register(CourseListCVC.self, forCellWithReuseIdentifier: CourseListCVC.className) } @@ -202,14 +206,14 @@ extension CourseDiscoveryVC { extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { - return 3 + return 5 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { switch section { - case 0, 1: + case 0, 1, 2, 3: return 1 - case 2: + case 4: return self.courseList.count default: return 0 @@ -221,6 +225,13 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AdImageCollectionViewCell.className, for: indexPath) as? AdImageCollectionViewCell else { return UICollectionViewCell() } return cell } else if indexPath.section == 1 { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonTitleCollectionViewCell.className, for: indexPath) as? MarathonTitleCollectionViewCell else { return UICollectionViewCell() } + return cell + } else if indexPath.section == 2 { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonMapCollectionViewCell.className, for: indexPath) as? MarathonMapCollectionViewCell else { return UICollectionViewCell() } + return cell + + } else if indexPath.section == 3 { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TitleCollectionViewCell.className, for: indexPath) as? TitleCollectionViewCell else { return UICollectionViewCell() } return cell } else { @@ -238,18 +249,18 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc let contentOffsetY = scrollView.contentOffset.y let collectionViewHeight = mapCollectionView.contentSize.height let paginationY = collectionViewHeight * 0.2 - + // 스크롤이 80% (0.2) 까지 도달하면 다음 페이지 데이터를 불러옵니다. if contentOffsetY >= collectionViewHeight - paginationY { if courseList.count < pageNo * 24 { // 페이지 끝에 도달하면 현재 페이지에 더 이상 데이터가 없음을 의미합니다. // 페이지네이션 중단 코드 return } - + // 다음 페이지 번호를 증가시킵니다. pageNo += 1 print("🔥다음 페이지 로드: \(pageNo)🔥") - + // 여기에서 다음 페이지 데이터를 불러오는 함수를 호출하세요. getCourseData() } @@ -268,10 +279,14 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { switch indexPath.section { case 0: - return CGSize(width: screenWidth, height: screenWidth * (183/390)) + return CGSize(width: screenWidth, height: screenWidth * (174/390)) case 1: - return CGSize(width: screenWidth, height: 80) + return CGSize(width: screenWidth, height: 98) case 2: + return CGSize(width: screenWidth, height: 160) + case 3: + return CGSize(width: screenWidth, height: 106) + case 4: let cellWidth = (screenWidth - 42) / 2 let cellHeight = CourseListCVCType.getCellHeight(type: .all, cellWidth: cellWidth) return CGSize(width: cellWidth, height: cellHeight) @@ -281,28 +296,28 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { - if section == 2 { + if section == 4 { return 20 } return 0 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { - if section == 2 { + if section == 4 { return 10 } return 0 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { - if section == 2 { + if section == 4 { return UIEdgeInsets(top: 0, left: 16, bottom: 20, right: 16) } return .zero } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - if indexPath.section == 2 { + if indexPath.section == 4 { let courseDetailVC = CourseDetailVC() let courseModel = courseList[indexPath.item] courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) From 11141a7020625f50df82b75a97e42157016ccfe3 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sat, 18 Nov 2023 17:54:41 +0900 Subject: [PATCH 02/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20Title=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/TitleCollectionViewCell.swift | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index 365c1c34..dd03808a 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -16,25 +16,39 @@ class TitleCollectionViewCell: UICollectionViewCell { private lazy var titleStackView = UIStackView(arrangedSubviews: [mainLabel, subLabel]).then { $0.axis = .vertical - $0.spacing = 4 + $0.spacing = 6 $0.alignment = .leading } + private let divideView = UIView() + private let mainLabel: UILabel = { let label = UILabel() label.text = "이런 코스 어때요?" - label.font = UIFont.h4 + label.font = UIFont.h3 label.textColor = UIColor.g1 return label }() private let subLabel: UILabel = { let label = UILabel() - label.text = "상쾌한 하루를 시작하게 만들어주는 러닝코스" + label.text = "나에게 최적화된 코스를 찾아보세요" label.font = UIFont.b6 - label.textColor = UIColor.g1 + label.textColor = UIColor.g2 return label }() + private lazy var dateSort = UIButton(type: .custom).then { + $0.setTitle("최신순", for: .normal) + $0.titleLabel?.font = .h5 + $0.setTitleColor(.g2, for: .normal) + } + + private lazy var scrapSort = UIButton(type: .custom).then { + $0.setTitle("스크랩순", for: .normal) + $0.titleLabel?.font = .h5 + $0.setTitleColor(.g2, for: .normal) + } + // MARK: - Life cycle override init(frame: CGRect) { @@ -53,11 +67,30 @@ extension TitleCollectionViewCell { func layout() { contentView.backgroundColor = .clear - contentView.addSubview(titleStackView) - + contentView.addSubviews(titleStackView, divideView, dateSort, scrapSort) + + divideView.backgroundColor = .g4 + titleStackView.snp.makeConstraints { $0.centerY.equalToSuperview() $0.leading.equalToSuperview().inset(16) } + + divideView.snp.makeConstraints { + $0.top.equalTo(titleStackView.snp.top).offset(-34) + $0.centerX.equalToSuperview().inset(16) + $0.width.equalTo(358) + $0.height.equalTo(1) + } + + dateSort.snp.makeConstraints { + $0.top.equalTo(divideView.snp.bottom).offset(54) + $0.leading.equalTo(titleStackView.snp.trailing).offset(57) + } + + scrapSort.snp.makeConstraints { + $0.top.equalTo(divideView.snp.bottom).offset(54) + $0.leading.equalTo(dateSort.snp.trailing).offset(8) + } } } From db7e87e8fcb4e2bdabac45df663d747b364a2a00 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sat, 18 Nov 2023 17:55:34 +0900 Subject: [PATCH 03/40] =?UTF-8?q?[Feat]=20#194=20-=20=EB=A7=88=EB=9D=BC?= =?UTF-8?q?=ED=86=A4=20Cell=20skeleton=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj | 12 ++++++++++-- .../Views/MarathonMapCollectionViewCell.swift | 12 ++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index cfc91627..09c7f9b1 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 712F661D2A7B7BAB00D9539B /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712F661C2A7B7BAB00D9539B /* Config.swift */; }; 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */; }; + 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; + 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */; }; A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */; }; A3BC2F2B2962C3D500198261 /* GoalRewardInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */; }; A3BC2F2D2962C3F200198261 /* ActivityRecordInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */; }; @@ -178,6 +180,8 @@ 712F661C2A7B7BAB00D9539B /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; + 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; + 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoDto.swift; sourceTree = ""; }; A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoVC.swift; sourceTree = ""; }; A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityRecordInfoVC.swift; sourceTree = ""; }; @@ -723,9 +727,11 @@ CE17F0442961C3D900E1DED0 /* Views */ = { isa = PBXGroup; children = ( - DAD5A3DB296C6DB800C8166B /* MapCollectionViewCell.swift */, - DAD5A3D9296C6DA500C8166B /* TitleCollectionViewCell.swift */, DAD5A3D7296C6D9600C8166B /* AdImageCollectionViewCell.swift */, + 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */, + 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */, + DAD5A3D9296C6DA500C8166B /* TitleCollectionViewCell.swift */, + DAD5A3DB296C6DB800C8166B /* MapCollectionViewCell.swift */, CE17F0432961C3D600E1DED0 /* VC */, ); path = Views; @@ -1363,6 +1369,7 @@ CE4545CD295D7AF4003201E1 /* TaBarController.swift in Sources */, A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */, CE21C024299E5FE500F62AF5 /* UserRouter.swift in Sources */, + 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */, CE6655F4295D898400C64E12 /* UIViewController+.swift in Sources */, CE14677829658C7200DCEA1B /* Stopwatch.swift in Sources */, CE21C026299E5FF300F62AF5 /* CourseRouter.swift in Sources */, @@ -1410,6 +1417,7 @@ CEC2A6922962BE2900160BF7 /* DepartureSearchVC.swift in Sources */, CE6655EE295D88E600C64E12 /* UITextField+.swift in Sources */, CE40BB1C2967E4910030ABCA /* RunningWaitingVC.swift in Sources */, + 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */, CE6B63D6296731F9003F900F /* ScrapCourseListView.swift in Sources */, CE6655F8295D90CF00C64E12 /* adjusted+.swift in Sources */, A3C2CAD729E53B2900EC525B /* RNAlertVC.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift new file mode 100644 index 00000000..26cc004c --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -0,0 +1,12 @@ +// +// MarathonMapCollectionViewCell.swift +// Runnect-iOS +// +// Created by 이명진 on 11/18/23. +// + +import UIKit + +class MarathonMapCollectionViewCell: UICollectionViewCell { + +} From ed1b4ca4f811597e041d493c9cb4a7fb5d61d393 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 21 Nov 2023 22:10:52 +0900 Subject: [PATCH 04/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=EC=BD=94=EC=8A=A4=20=EC=88=98=ED=8F=89=20CVC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서버 API 연결 전 테스트 코드 입니다. --- .../Runnect-iOS.xcodeproj/project.pbxproj | 12 +- .../Views/MarathonMapCollectionViewCell.swift | 12 -- .../RecommendedMapCollectionViewCell.swift | 154 ++++++++++++++++++ 3 files changed, 162 insertions(+), 16 deletions(-) delete mode 100644 Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift create mode 100644 Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 09c7f9b1..d7a9797b 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -17,7 +17,8 @@ 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */; }; 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; - 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */; }; + 71F780502B0893D700B53253 /* RecommendedMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* RecommendedMapCollectionViewCell.swift */; }; + 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */; }; A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */; }; A3BC2F2B2962C3D500198261 /* GoalRewardInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */; }; A3BC2F2D2962C3F200198261 /* ActivityRecordInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */; }; @@ -181,7 +182,8 @@ 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; - 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; + 71F7804F2B0893D700B53253 /* RecommendedMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedMapCollectionViewCell.swift; sourceTree = ""; }; + 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedListCVC.swift; sourceTree = ""; }; A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoDto.swift; sourceTree = ""; }; A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoVC.swift; sourceTree = ""; }; A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityRecordInfoVC.swift; sourceTree = ""; }; @@ -729,7 +731,7 @@ children = ( DAD5A3D7296C6D9600C8166B /* AdImageCollectionViewCell.swift */, 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */, - 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */, + 71F7804F2B0893D700B53253 /* RecommendedMapCollectionViewCell.swift */, DAD5A3D9296C6DA500C8166B /* TitleCollectionViewCell.swift */, DAD5A3DB296C6DB800C8166B /* MapCollectionViewCell.swift */, CE17F0432961C3D600E1DED0 /* VC */, @@ -1162,6 +1164,7 @@ isa = PBXGroup; children = ( CE6B63D2296725E6003F900F /* CourseListCVC.swift */, + 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */, ); path = CVC; sourceTree = ""; @@ -1369,7 +1372,7 @@ CE4545CD295D7AF4003201E1 /* TaBarController.swift in Sources */, A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */, CE21C024299E5FE500F62AF5 /* UserRouter.swift in Sources */, - 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */, + 71F780502B0893D700B53253 /* RecommendedMapCollectionViewCell.swift in Sources */, CE6655F4295D898400C64E12 /* UIViewController+.swift in Sources */, CE14677829658C7200DCEA1B /* Stopwatch.swift in Sources */, CE21C026299E5FF300F62AF5 /* CourseRouter.swift in Sources */, @@ -1451,6 +1454,7 @@ A3D1A77E29CF09B600DD54EC /* SignInResponseDto.swift in Sources */, CE21C028299E5FFC00F62AF5 /* PublicCourseRouter.swift in Sources */, CE18E894296C79B900FEB569 /* CourseDrawingRequestDto.swift in Sources */, + 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */, DA20D84E2966A9B300F1581F /* CourseSearchVC.swift in Sources */, CE1006572968230800FD31FB /* DepartureLocationModel.swift in Sources */, CECA695C296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift deleted file mode 100644 index 26cc004c..00000000 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// MarathonMapCollectionViewCell.swift -// Runnect-iOS -// -// Created by 이명진 on 11/18/23. -// - -import UIKit - -class MarathonMapCollectionViewCell: UICollectionViewCell { - -} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift new file mode 100644 index 00000000..5f0d1343 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift @@ -0,0 +1,154 @@ +// +// RecommendedMapCollectionViewCell.swift +// Runnect-iOS +// +// Created by 이명진 on 11/18/23. +// + +import UIKit +import SnapKit +import Then + +class RecommendedMapCollectionViewCell: UICollectionViewCell { + private let PublicCourseProvider = Providers.publicCourseProvider + + private lazy var recommendedCollectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .clear + collectionView.isScrollEnabled = true + collectionView.showsHorizontalScrollIndicator = false + return collectionView + }() + + // MARK: - Constants + + final let collectionViewInset = UIEdgeInsets(top: 0, left: 16, bottom: 34, right: 16) + final let itemSpacing: CGFloat = 10 + final let lineSpacing: CGFloat = 10 + private var courseList = [PublicCourse]() + + // MARK: - Life cycle + override init(frame: CGRect) { + super.init(frame: frame) + layout() + register() + setDelegate() + getCourseData() + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension RecommendedMapCollectionViewCell { + + private func setDelegate() { + recommendedCollectionView.delegate = self + recommendedCollectionView.dataSource = self + } + private func register() { + recommendedCollectionView.register(CourseListCVC.self, + forCellWithReuseIdentifier: CourseListCVC.className) + } +} +// MARK: - Extensions + +extension RecommendedMapCollectionViewCell { + + // MARK: - Layout Helpers + + func layout() { + contentView.backgroundColor = .clear + contentView.addSubview(recommendedCollectionView) + recommendedCollectionView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.leading.trailing.equalTo(contentView.safeAreaLayoutGuide) + $0.bottom.equalToSuperview() + } + } + + func setData(courseList: [PublicCourse]) { + self.courseList = courseList + recommendedCollectionView.reloadData() + } + +} +// MARK: - UICollectionViewDelegate, UICollectionViewDataSource + +extension RecommendedMapCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return courseList.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, + for: indexPath) + as? CourseListCVC else { return UICollectionViewCell() } + cell.setCellType(type: .all) + let model = self.courseList[indexPath.item] + let location = "\(model.departure.region) \(model.departure.city)" + cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scrap, indexPath: indexPath.item) + return cell + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension RecommendedMapCollectionViewCell: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: 156, height: 160) + } // 셀사이즈 RecommendedCVC와 연결 되면 변경 + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return self.collectionViewInset + } + + // 이게 반대방향 + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return self.itemSpacing + } + + // 이게 스크롤방향 + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return self.lineSpacing + } + +} + +// 원래 서버에서 1페이지만 가져온 데이터로 테스트 + +extension RecommendedMapCollectionViewCell { + private func getCourseData() { + LoadingIndicator.showLoading() + PublicCourseProvider.request(.getCourseData(pageNo: 1)) { response in + LoadingIndicator.hideLoading() + switch response { + case .success(let result): + let status = result.statusCode + if 200..<300 ~= status { + do { + let responseDto = try result.map(BaseResponse.self) + guard let data = responseDto.data else { return } + + // 새로 받은 데이터를 기존 리스트에 추가 (쌓기 위함) + self.courseList.append(contentsOf: data.publicCourses) + + // UI를 업데이트하여 추가된 데이터를 반영합니다. + self.setData(courseList: self.courseList) + + } catch { + print(error.localizedDescription) + } + } + if status >= 400 { + print("400 error") + } + case .failure(let error): + print(error.localizedDescription) + } + } + } + +} From e938b969a86629d63530b94ae3e5a4c362ea015a Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 21 Nov 2023 22:13:30 +0900 Subject: [PATCH 05/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=EC=88=98=ED=8F=89=20CVC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDiscovery/Views/MapCollectionViewCell.swift | 3 +++ .../CourseDiscovery/Views/VC/CourseDiscoveryVC.swift | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift index 7bf86702..132bd686 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift @@ -5,6 +5,9 @@ // Created by YEONOO on 2023/01/10. // +/// 여기 있는 코드는 사용되지 않습니다. +/// CourseDiscoveryVC에 따로 선언 되어있습니다. + import UIKit import SnapKit diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index f46b120c..d1d7c6d4 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -97,7 +97,7 @@ extension CourseDiscoveryVC { self.mapCollectionView.register(AdImageCollectionViewCell.self, forCellWithReuseIdentifier: AdImageCollectionViewCell.className) self.mapCollectionView.register(MarathonTitleCollectionViewCell.self, forCellWithReuseIdentifier: MarathonTitleCollectionViewCell.className) - self.mapCollectionView.register(MarathonMapCollectionViewCell.self, forCellWithReuseIdentifier: MarathonMapCollectionViewCell.className) + self.mapCollectionView.register(RecommendedMapCollectionViewCell.self, forCellWithReuseIdentifier: RecommendedMapCollectionViewCell.className) self.mapCollectionView.register(TitleCollectionViewCell.self, forCellWithReuseIdentifier: TitleCollectionViewCell.className) self.mapCollectionView.register(CourseListCVC.self, forCellWithReuseIdentifier: CourseListCVC.className) } @@ -228,7 +228,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonTitleCollectionViewCell.className, for: indexPath) as? MarathonTitleCollectionViewCell else { return UICollectionViewCell() } return cell } else if indexPath.section == 2 { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonMapCollectionViewCell.className, for: indexPath) as? MarathonMapCollectionViewCell else { return UICollectionViewCell() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RecommendedMapCollectionViewCell.className, for: indexPath) as? RecommendedMapCollectionViewCell else { return UICollectionViewCell() } return cell } else if indexPath.section == 3 { @@ -283,7 +283,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { case 1: return CGSize(width: screenWidth, height: 98) case 2: - return CGSize(width: screenWidth, height: 160) + return CGSize(width: screenWidth, height: 194) case 3: return CGSize(width: screenWidth, height: 106) case 4: @@ -317,7 +317,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - if indexPath.section == 4 { + if indexPath.section == 2 || indexPath.section == 4 { let courseDetailVC = CourseDetailVC() let courseModel = courseList[indexPath.item] courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) From 1edc88aa3eeee6071359e4290838633dfb071b7c Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 21 Nov 2023 22:47:58 +0900 Subject: [PATCH 06/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=EC=BD=94=EC=8A=A4=EC=9D=98=20=EC=85=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 추천코스, 수평 스크롤 인점 감안하여 셀을 추가하였습니다. 이 부분은 추천코스의 역할을 할 예정입니다. --- .../Views/CVC/RecommendedListCVC.swift | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift new file mode 100644 index 00000000..9e783eee --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift @@ -0,0 +1,202 @@ +// +// RecommendedListCVC.swift +// Runnect-iOS +// +// Created by 이명진 on 11/21/23. +// + +import UIKit + +protocol RecommendedListCVCDeleagte: AnyObject { + func likeButtonTapped(wantsTolike: Bool, index: Int) +} + +@frozen +enum RecommendedListCVCType { + case title + case titleWithLocation + case all + + static func getCellHeight(type: CourseListCVCType, cellWidth: CGFloat) -> CGFloat { + let imageHeight = cellWidth * (111/156) + switch type { + case .title: + return imageHeight + 24 + case .titleWithLocation, .all: + return imageHeight + 40 + } + } +} + +final class RecommendedListCVC: UICollectionViewCell { + + // MARK: - Properties + + weak var delegate: RecommendedListCVCDeleagte? + + private var indexPath: Int? + + // MARK: - UI Components + + private let courseImageView = UIImageView().then { + $0.backgroundColor = .g3 + $0.contentMode = .scaleToFill + $0.layer.cornerRadius = 5 + $0.clipsToBounds = true + } + + private let imageCoverView = UIImageView().then { + $0.backgroundColor = .m1.withAlphaComponent(0.2) + $0.layer.cornerRadius = 5 + $0.isHidden = true + } + + private let titleLabel = UILabel().then { + $0.text = "제목" + $0.font = .b4 + $0.textColor = .g1 + } + + private let locationLabel = UILabel().then { + $0.text = "위치" + $0.font = .b6 + $0.textColor = .g2 + } + + private lazy var labelStackView = UIStackView( + arrangedSubviews: [titleLabel, locationLabel] + ).then { + $0.axis = .vertical + $0.alignment = .leading + } + + private let likeButton = UIButton(type: .custom).then { + $0.setImage(ImageLiterals.icHeartFill, for: .selected) + $0.setImage(ImageLiterals.icHeart, for: .normal) + $0.backgroundColor = .w1 + } + + private let selectIndicatorButton = UIButton(type: .custom).then { + $0.setImage(ImageLiterals.icCheckFill, for: .selected) + $0.setImage(ImageLiterals.icCheck, for: .normal) + $0.isSelected = false + $0.isHidden = true + } + + // MARK: - initialization + + override init(frame: CGRect) { + super.init(frame: frame) + self.setUI() + self.setLayout() + self.setAddTarget() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Methods + +extension RecommendedListCVC { + private func setAddTarget() { + likeButton.addTarget(self, action: #selector(likeButtonDidTap), for: .touchUpInside) + } + + func setData(imageURL: String, title: String, location: String?, didLike: Bool?, indexPath: Int? = nil, isEditMode: Bool = false) { + self.courseImageView.setImage(with: imageURL) + self.titleLabel.text = title + self.indexPath = indexPath + if let location = location { + self.locationLabel.text = location + } + if let didLike = didLike { + self.likeButton.isSelected = didLike + } + self.selectIndicatorButton.isHidden = !isEditMode + } + + func selectCell(didSelect: Bool) { + if didSelect { + courseImageView.layer.borderColor = UIColor.m1.cgColor + courseImageView.layer.borderWidth = 2 + imageCoverView.isHidden = false + selectIndicatorButton.isSelected = true + } else { + courseImageView.layer.borderColor = UIColor.clear.cgColor + imageCoverView.isHidden = true + selectIndicatorButton.isSelected = false + + } + } +} + +// MARK: - @objc Function + +extension RecommendedListCVC { + @objc func likeButtonDidTap(_ sender: UIButton) { + guard let indexPath = self.indexPath else { return } + if UserManager.shared.userType != .visitor { + sender.isSelected.toggle() + } + delegate?.likeButtonTapped(wantsTolike: (sender.isSelected == true), index: indexPath) + } +} + +// MARK: - UI & Layout + +extension RecommendedListCVC { + private func setUI() { + self.contentView.backgroundColor = .w1 + } + + private func setLayout() { + self.contentView.addSubviews(courseImageView, imageCoverView, labelStackView, likeButton, selectIndicatorButton) + + courseImageView.snp.makeConstraints { make in + make.leading.top.trailing.equalToSuperview() + let imageHeight = contentView.frame.width * (111/156) + make.height.equalTo(imageHeight) + } + + imageCoverView.snp.makeConstraints { make in + make.edges.equalTo(courseImageView) + } + + likeButton.snp.makeConstraints { make in + make.top.equalTo(courseImageView.snp.bottom).offset(4) + make.trailing.equalToSuperview() + make.width.equalTo(22) + make.height.equalTo(20) + } + + selectIndicatorButton.snp.makeConstraints { make in + make.top.leading.equalToSuperview().inset(8) + make.leading.equalToSuperview().offset(8) + make.width.equalTo(20) + make.height.equalTo(20) + } + + labelStackView.snp.makeConstraints { make in + make.top.equalTo(courseImageView.snp.bottom).offset(4) + make.leading.equalToSuperview() + make.width.equalTo(courseImageView.snp.width).multipliedBy(0.7) + } + } + + func setCellType(type: RecommendedListCVCType) { + switch type { + case .title: + self.locationLabel.isHidden = true + self.likeButton.isHidden = true + case .titleWithLocation: + self.locationLabel.isHidden = false + self.likeButton.isHidden = true + case .all: + self.locationLabel.isHidden = false + self.likeButton.isHidden = false + } + } +} From 42b14a65974b96a528cfd92ef3534334656f3709 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 21 Nov 2023 23:17:59 +0900 Subject: [PATCH 07/40] =?UTF-8?q?[Refactor]=20#194=20-=20=EC=BD=94?= =?UTF-8?q?=EC=8A=A4=20=EB=B0=9C=EA=B2=AC=20=EC=BD=94=EB=93=9C=20=EC=83=81?= =?UTF-8?q?=EC=88=98=ED=99=94=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다양한 케이스가 매직넘버로 이루어져 있어, 상수로 대체하여 가독성을 증가시켰습니다. --- .../Views/VC/CourseDiscoveryVC.swift | 116 +++++++++--------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index d1d7c6d4..5feff1ad 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -13,19 +13,14 @@ import Combine import Moya final class CourseDiscoveryVC: UIViewController { - // MARK: - Properties - private let PublicCourseProvider = Providers.publicCourseProvider + // MARK: - Properties + private let publicCourseProvider = Providers.publicCourseProvider private let scrapProvider = Providers.scrapProvider - private var courseList = [PublicCourse]() - private var specialList = [String]() - - // pagination 에 꼭 필요한 위한 변수들 입니다. private var pageNo = 1 - private var isDataLoaded = false // MARK: - UIComponents @@ -58,6 +53,7 @@ final class CourseDiscoveryVC: UIViewController { return collectionView }() + // MARK: - View Life Cycle override func viewDidLoad () { @@ -95,11 +91,14 @@ extension CourseDiscoveryVC { private func register() { - self.mapCollectionView.register(AdImageCollectionViewCell.self, forCellWithReuseIdentifier: AdImageCollectionViewCell.className) - self.mapCollectionView.register(MarathonTitleCollectionViewCell.self, forCellWithReuseIdentifier: MarathonTitleCollectionViewCell.className) - self.mapCollectionView.register(RecommendedMapCollectionViewCell.self, forCellWithReuseIdentifier: RecommendedMapCollectionViewCell.className) - self.mapCollectionView.register(TitleCollectionViewCell.self, forCellWithReuseIdentifier: TitleCollectionViewCell.className) - self.mapCollectionView.register(CourseListCVC.self, forCellWithReuseIdentifier: CourseListCVC.className) + let cellTypes: [UICollectionViewCell.Type] = [AdImageCollectionViewCell.self, + MarathonTitleCollectionViewCell.self, + RecommendedMapCollectionViewCell.self, + TitleCollectionViewCell.self, + CourseListCVC.self] + cellTypes.forEach { cellType in + mapCollectionView.register(cellType, forCellWithReuseIdentifier: cellType.className) + } } private func setAddTarget() { @@ -109,17 +108,11 @@ extension CourseDiscoveryVC { private func setDataLoadIfNeeded() { /// 데이터를 받고 다른 뷰를 갔다가 와도 데이터가 유지되게끔 하기 위한 함수 입니다. (한번만 호출되면 되는 함수!) if !isDataLoaded { - // 앱이 실행 될때 처음에만 데이터 초기화 courseList.removeAll() pageNo = 1 - - // 컬렉션 뷰를 리로드하여 초기화된 데이터를 화면에 표시 mapCollectionView.reloadData() - self.getCourseData() - - isDataLoaded = true // 데이터가 로드되었음을 표시 - } else { - return + getCourseData() + isDataLoaded = true } } } @@ -190,7 +183,7 @@ extension CourseDiscoveryVC { let shadowView = ShadowView() self.view.addSubview(shadowView) - + shadowView.snp.makeConstraints { make in make.trailing.equalTo(self.view.safeAreaLayoutGuide).inset(16) make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) @@ -202,6 +195,24 @@ extension CourseDiscoveryVC { } } +// MARK: - Constants + +extension CourseDiscoveryVC { + private enum Section { + static let adImage = 0 + static let marathonTitle = 1 + static let recommendedList = 2 + static let title = 3 + static let courseList = 4 + } + + private enum Layout { + static let cellSpacing: CGFloat = 20 + static let interitemSpacing: CGFloat = 10 + static let sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 20, right: 16) + } +} + // MARK: - UICollectionViewDelegate, UICollectionViewDataSource extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSource { @@ -211,9 +222,9 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { switch section { - case 0, 1, 2, 3: + case Section.adImage, Section.marathonTitle, Section.recommendedList, Section.title: return 1 - case 4: + case Section.courseList: return self.courseList.count default: return 0 @@ -221,20 +232,20 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - if indexPath.section == 0 { + switch indexPath.section { + case Section.adImage: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AdImageCollectionViewCell.className, for: indexPath) as? AdImageCollectionViewCell else { return UICollectionViewCell() } return cell - } else if indexPath.section == 1 { + case Section.marathonTitle: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonTitleCollectionViewCell.className, for: indexPath) as? MarathonTitleCollectionViewCell else { return UICollectionViewCell() } return cell - } else if indexPath.section == 2 { + case Section.recommendedList: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RecommendedMapCollectionViewCell.className, for: indexPath) as? RecommendedMapCollectionViewCell else { return UICollectionViewCell() } return cell - - } else if indexPath.section == 3 { + case Section.title: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TitleCollectionViewCell.className, for: indexPath) as? TitleCollectionViewCell else { return UICollectionViewCell() } return cell - } else { + case Section.courseList: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .all) cell.delegate = self @@ -242,6 +253,8 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc let location = "\(model.departure.region) \(model.departure.city)" cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scrap, indexPath: indexPath.item) return cell + default: + return UICollectionViewCell() } } @@ -249,44 +262,41 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc let contentOffsetY = scrollView.contentOffset.y let collectionViewHeight = mapCollectionView.contentSize.height let paginationY = collectionViewHeight * 0.2 - + // 스크롤이 80% (0.2) 까지 도달하면 다음 페이지 데이터를 불러옵니다. if contentOffsetY >= collectionViewHeight - paginationY { if courseList.count < pageNo * 24 { // 페이지 끝에 도달하면 현재 페이지에 더 이상 데이터가 없음을 의미합니다. // 페이지네이션 중단 코드 return } - + // 다음 페이지 번호를 증가시킵니다. pageNo += 1 print("🔥다음 페이지 로드: \(pageNo)🔥") - + // 여기에서 다음 페이지 데이터를 불러오는 함수를 호출하세요. getCourseData() } } - + } // MARK: - UICollectionViewDelegateFlowLayout extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - _ = UICollectionViewCell() - let screenWidth = UIScreen.main.bounds.width switch indexPath.section { - case 0: + case Section.adImage: return CGSize(width: screenWidth, height: screenWidth * (174/390)) - case 1: + case Section.marathonTitle: return CGSize(width: screenWidth, height: 98) - case 2: + case Section.recommendedList: return CGSize(width: screenWidth, height: 194) - case 3: + case Section.title: return CGSize(width: screenWidth, height: 106) - case 4: + case Section.courseList: let cellWidth = (screenWidth - 42) / 2 let cellHeight = CourseListCVCType.getCellHeight(type: .all, cellWidth: cellWidth) return CGSize(width: cellWidth, height: cellHeight) @@ -296,33 +306,25 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { - if section == 4 { - return 20 - } - return 0 + return section == Section.courseList ? Layout.cellSpacing : 0 } + // section 이 4일때만 정해진 레이아웃 리턴 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { - if section == 4 { - return 10 - } - return 0 + return section == Section.courseList ? Layout.interitemSpacing : 0 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { - if section == 4 { - return UIEdgeInsets(top: 0, left: 16, bottom: 20, right: 16) - } - return .zero + return section == Section.courseList ? Layout.sectionInset : .zero } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - if indexPath.section == 2 || indexPath.section == 4 { + if indexPath.section == Section.recommendedList || indexPath.section == Section.courseList { let courseDetailVC = CourseDetailVC() let courseModel = courseList[indexPath.item] courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) courseDetailVC.hidesBottomBarWhenPushed = true - self.navigationController?.pushViewController(courseDetailVC, animated: true) + navigationController?.pushViewController(courseDetailVC, animated: true) } } } @@ -346,7 +348,7 @@ extension CourseDiscoveryVC: CourseListCVCDeleagte { extension CourseDiscoveryVC { private func getCourseData() { LoadingIndicator.showLoading() - PublicCourseProvider.request(.getCourseData(pageNo: pageNo)) { response in + publicCourseProvider.request(.getCourseData(pageNo: pageNo)) { response in LoadingIndicator.hideLoading() switch response { case .success(let result): @@ -376,7 +378,7 @@ extension CourseDiscoveryVC { } } } - + private func scrapCourse(publicCourseId: Int, scrapTF: Bool) { LoadingIndicator.showLoading() scrapProvider.request(.createAndDeleteScrap(publicCourseId: publicCourseId, scrapTF: scrapTF)) { [weak self] response in From 2cbd292962ae72c318b4e8aefc02b3c84bd82073 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 21 Nov 2023 23:26:49 +0900 Subject: [PATCH 08/40] =?UTF-8?q?[Refactor]=20#194=20-=20Then=20=EB=AC=B8?= =?UTF-8?q?=EB=B2=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TitleCollectionViewCell의 then으로 안된 문법을 변경해 주었습니다. --- .../Views/TitleCollectionViewCell.swift | 34 ++++++++++--------- .../Views/VC/CourseDiscoveryVC.swift | 4 +-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index dd03808a..a45a63b2 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -2,7 +2,7 @@ // TitleCollectionViewCell.swift // Runnect-iOS // -// Created by YEONOO on 2023/01/10. +// Created by 이명진 on 2023/11/21. // import UIKit @@ -20,20 +20,25 @@ class TitleCollectionViewCell: UICollectionViewCell { $0.alignment = .leading } - private let divideView = UIView() + private let divideView = UIView().then { + $0.backgroundColor = .g4 + } private let mainLabel: UILabel = { - let label = UILabel() - label.text = "이런 코스 어때요?" - label.font = UIFont.h3 - label.textColor = UIColor.g1 + let label = UILabel().then { + $0.text = "이런 코스 어때요?" + $0.font = UIFont.h3 + $0.textColor = UIColor.g1 + } return label }() + private let subLabel: UILabel = { - let label = UILabel() - label.text = "나에게 최적화된 코스를 찾아보세요" - label.font = UIFont.b6 - label.textColor = UIColor.g2 + let label = UILabel().then { + $0.text = "나에게 최적화된 코스를 찾아보세요" + $0.font = UIFont.b6 + $0.textColor = UIColor.g2 + } return label }() @@ -55,22 +60,19 @@ class TitleCollectionViewCell: UICollectionViewCell { super.init(frame: frame) layout() } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } -// MARK: - Extensions + +// MARK: - Layout extension TitleCollectionViewCell { - - // MARK: - Layout Helpers - func layout() { contentView.backgroundColor = .clear contentView.addSubviews(titleStackView, divideView, dateSort, scrapSort) - divideView.backgroundColor = .g4 - titleStackView.snp.makeConstraints { $0.centerY.equalToSuperview() $0.leading.equalToSuperview().inset(16) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 5feff1ad..58204706 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -2,7 +2,7 @@ // CourseDiscoveryVC.swift // Runnect-iOS // -// Created by sejin on 2023/01/01. +// Created by 이명진 on 2023/11/21. // import UIKit @@ -305,10 +305,10 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } } + /// section 이 4일때만 정해진 레이아웃 리턴 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return section == Section.courseList ? Layout.cellSpacing : 0 } - // section 이 4일때만 정해진 레이아웃 리턴 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return section == Section.courseList ? Layout.interitemSpacing : 0 From 14c53931d08942412c014eb6bf52d75844e53495 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 23 Nov 2023 17:24:58 +0900 Subject: [PATCH 09/40] =?UTF-8?q?[Chore]=20#194=20-=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=20=ED=86=B5=EC=8B=A0=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 추후 불필요한 메시지(코드) 삭제 예정 --- .../Global/Utils/RNUtils/UserManager.swift | 21 +++++++++++++++++++ .../Network/Service/AuthInterceptor.swift | 2 +- .../Views/MapCollectionViewCell.swift | 1 + .../Views/TitleCollectionViewCell.swift | 8 ++++--- .../SignIn/VC/SignInSocialLoginVC.swift | 3 +++ 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift index 50305d6f..31570cb8 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift @@ -53,6 +53,7 @@ final class UserManager { self.isKakao = provider == "KAKAO" ? true : false UserManager.shared.userType = .registered completion(.success(data.type)) // 로그인인지 회원가입인지 전달 + print("\n\n 🥰\(String(describing: data.email))\n\n") } catch { print(error.localizedDescription) completion(.failure(.networkFail)) @@ -64,6 +65,16 @@ final class UserManager { } case .failure(let error): print(error.localizedDescription) + if let response = error.response { + if let responseData = String(data: response.data, encoding: .utf8) { + print("\n\n SignIn 메세지 ‼️🔥\(responseData)\n\n") + } else { + print(error.localizedDescription) + } + } else { + print(error.localizedDescription) + } + completion(.failure(.networkFail)) } } @@ -92,6 +103,16 @@ final class UserManager { completion(.failure(.networkFail)) } case .failure(let error): + if let response = error.response { + if let responseData = String(data: response.data, encoding: .utf8) { + print("\n\n getNewToken 메세지 ‼️🔥\(responseData)\n\n") + } else { + print(error.localizedDescription) + } + } else { + print(error.localizedDescription) + } + print(error.localizedDescription) completion(.failure(.networkFail)) } diff --git a/Runnect-iOS/Runnect-iOS/Network/Service/AuthInterceptor.swift b/Runnect-iOS/Runnect-iOS/Network/Service/AuthInterceptor.swift index 41be8ff1..0f617ef6 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Service/AuthInterceptor.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Service/AuthInterceptor.swift @@ -23,7 +23,7 @@ final class AuthInterceptor: RequestInterceptor { // 방문자일 경우 if UserManager.shared.userType == .visitor && urlRequest.url?.absoluteString.hasPrefix(Config.baseURL) == true { urlRequest.setValue("visitor", forHTTPHeaderField: "accessToken") - urlRequest.setValue("null", forHTTPHeaderField: "refreshToken") + urlRequest.setValue("visitor", forHTTPHeaderField: "refreshToken") completion(.success(urlRequest)) return } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift index 132bd686..13d35d1b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift @@ -7,6 +7,7 @@ /// 여기 있는 코드는 사용되지 않습니다. /// CourseDiscoveryVC에 따로 선언 되어있습니다. +/// 추가 사용안할시 삭제 예정 import UIKit import SnapKit diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index a45a63b2..f5e0afb4 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -44,14 +44,16 @@ class TitleCollectionViewCell: UICollectionViewCell { private lazy var dateSort = UIButton(type: .custom).then { $0.setTitle("최신순", for: .normal) - $0.titleLabel?.font = .h5 - $0.setTitleColor(.g2, for: .normal) + $0.titleLabel?.font = .b3 + $0.setTitleColor(.m1, for: .normal) + $0.setTitleColor(.g2, for: .disabled) } private lazy var scrapSort = UIButton(type: .custom).then { $0.setTitle("스크랩순", for: .normal) - $0.titleLabel?.font = .h5 + $0.titleLabel?.font = .b3 $0.setTitleColor(.g2, for: .normal) + $0.setTitleColor(.m1, for: .disabled) } // MARK: - Life cycle diff --git a/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift index fa94ab0d..38843ac1 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift @@ -211,12 +211,15 @@ extension SignInSocialLoginVC: ASAuthorizationControllerPresentationContextProvi print("token : \(String(describing: tokeStr))") UserManager.shared.signIn(token: tokeStr, provider: "APPLE") { [weak self] result in + switch result { case .success(let type): + type == "Signup" ? self?.pushToNickNameSetUpVC() : self?.pushToTabBarController() case .failure(let error): print(error) self?.showNetworkFailureToast() + } } default: From 0df28bf7b36343d10970048bdfa27ea3899599ad Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 23 Nov 2023 21:14:58 +0900 Subject: [PATCH 10/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=EC=A0=95=EB=A0=AC=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runnect-iOS.xcodeproj/project.pbxproj | 8 ++++---- .../Network/Router/PublicCourseRouter.swift | 9 +++++---- ...ift => MarathonMapCollectionViewCell.swift} | 16 ++++++++-------- .../Views/VC/CourseDiscoveryVC.swift | 18 +++++++++++++++--- 4 files changed, 32 insertions(+), 19 deletions(-) rename Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/{RecommendedMapCollectionViewCell.swift => MarathonMapCollectionViewCell.swift} (91%) diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index d7a9797b..e6704079 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -17,7 +17,7 @@ 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */; }; 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; - 71F780502B0893D700B53253 /* RecommendedMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* RecommendedMapCollectionViewCell.swift */; }; + 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */; }; 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */; }; A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */; }; A3BC2F2B2962C3D500198261 /* GoalRewardInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */; }; @@ -182,7 +182,7 @@ 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; - 71F7804F2B0893D700B53253 /* RecommendedMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedMapCollectionViewCell.swift; sourceTree = ""; }; + 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedListCVC.swift; sourceTree = ""; }; A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoDto.swift; sourceTree = ""; }; A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoVC.swift; sourceTree = ""; }; @@ -731,7 +731,7 @@ children = ( DAD5A3D7296C6D9600C8166B /* AdImageCollectionViewCell.swift */, 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */, - 71F7804F2B0893D700B53253 /* RecommendedMapCollectionViewCell.swift */, + 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */, DAD5A3D9296C6DA500C8166B /* TitleCollectionViewCell.swift */, DAD5A3DB296C6DB800C8166B /* MapCollectionViewCell.swift */, CE17F0432961C3D600E1DED0 /* VC */, @@ -1372,7 +1372,7 @@ CE4545CD295D7AF4003201E1 /* TaBarController.swift in Sources */, A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */, CE21C024299E5FE500F62AF5 /* UserRouter.swift in Sources */, - 71F780502B0893D700B53253 /* RecommendedMapCollectionViewCell.swift in Sources */, + 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */, CE6655F4295D898400C64E12 /* UIViewController+.swift in Sources */, CE14677829658C7200DCEA1B /* Stopwatch.swift in Sources */, CE21C026299E5FF300F62AF5 /* CourseRouter.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift index 3c336277..7369d62b 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift @@ -9,14 +9,14 @@ import Foundation import Moya enum PublicCourseRouter { - case getCourseData(pageNo: Int) + case getCourseData(pageNo: Int, sort: String) case getCourseSearchData(keyword: String) case courseUploadingData(param: CourseUploadingRequestDto) case getUploadedCourseDetail(publicCourseId: Int) case getUploadedCourseInfo case updatePublicCourse(publicCourseId: Int, editCourseRequestDto: EditCourseRequestDto) case deleteUploadedCourse(publicCourseIdList: [Int]) -} +} extension PublicCourseRouter: TargetType { @@ -59,8 +59,9 @@ extension PublicCourseRouter: TargetType { var task: Moya.Task { switch self { - case .getCourseData(let pageNo): - return .requestParameters(parameters: ["pageNo": pageNo], encoding: URLEncoding.default) + case .getCourseData(let pageNo, let sort): + var parameters: [String: Any] = ["pageNo": pageNo, "sort": sort] + return .requestParameters(parameters: parameters, encoding: URLEncoding.default) case .getCourseSearchData(let keyword): return .requestParameters(parameters: ["keyword": keyword], encoding: URLEncoding.default) case .courseUploadingData(param: let param): diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift similarity index 91% rename from Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift rename to Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift index 5f0d1343..1dbe7995 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/RecommendedMapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -1,5 +1,5 @@ // -// RecommendedMapCollectionViewCell.swift +// MarathonMapCollectionViewCell.swift // Runnect-iOS // // Created by 이명진 on 11/18/23. @@ -9,7 +9,7 @@ import UIKit import SnapKit import Then -class RecommendedMapCollectionViewCell: UICollectionViewCell { +class MarathonMapCollectionViewCell: UICollectionViewCell { private let PublicCourseProvider = Providers.publicCourseProvider private lazy var recommendedCollectionView: UICollectionView = { @@ -42,7 +42,7 @@ class RecommendedMapCollectionViewCell: UICollectionViewCell { } } -extension RecommendedMapCollectionViewCell { +extension MarathonMapCollectionViewCell { private func setDelegate() { recommendedCollectionView.delegate = self @@ -55,7 +55,7 @@ extension RecommendedMapCollectionViewCell { } // MARK: - Extensions -extension RecommendedMapCollectionViewCell { +extension MarathonMapCollectionViewCell { // MARK: - Layout Helpers @@ -77,7 +77,7 @@ extension RecommendedMapCollectionViewCell { } // MARK: - UICollectionViewDelegate, UICollectionViewDataSource -extension RecommendedMapCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource { +extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return courseList.count } @@ -96,7 +96,7 @@ extension RecommendedMapCollectionViewCell: UICollectionViewDelegate, UICollecti // MARK: - UICollectionViewDelegateFlowLayout -extension RecommendedMapCollectionViewCell: UICollectionViewDelegateFlowLayout { +extension MarathonMapCollectionViewCell: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: 156, height: 160) } // 셀사이즈 RecommendedCVC와 연결 되면 변경 @@ -119,10 +119,10 @@ extension RecommendedMapCollectionViewCell: UICollectionViewDelegateFlowLayout { // 원래 서버에서 1페이지만 가져온 데이터로 테스트 -extension RecommendedMapCollectionViewCell { +extension MarathonMapCollectionViewCell { private func getCourseData() { LoadingIndicator.showLoading() - PublicCourseProvider.request(.getCourseData(pageNo: 1)) { response in + PublicCourseProvider.request(.getCourseData(pageNo: 1, sort: "date")) { response in LoadingIndicator.hideLoading() switch response { case .success(let result): diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 58204706..7e60f295 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -21,6 +21,7 @@ final class CourseDiscoveryVC: UIViewController { private var courseList = [PublicCourse]() private var specialList = [String]() private var pageNo = 1 + private var sort = "date" private var isDataLoaded = false // MARK: - UIComponents @@ -93,7 +94,7 @@ extension CourseDiscoveryVC { let cellTypes: [UICollectionViewCell.Type] = [AdImageCollectionViewCell.self, MarathonTitleCollectionViewCell.self, - RecommendedMapCollectionViewCell.self, + MarathonMapCollectionViewCell.self, TitleCollectionViewCell.self, CourseListCVC.self] cellTypes.forEach { cellType in @@ -240,10 +241,11 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonTitleCollectionViewCell.className, for: indexPath) as? MarathonTitleCollectionViewCell else { return UICollectionViewCell() } return cell case Section.recommendedList: - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RecommendedMapCollectionViewCell.className, for: indexPath) as? RecommendedMapCollectionViewCell else { return UICollectionViewCell() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonMapCollectionViewCell.className, for: indexPath) as? MarathonMapCollectionViewCell else { return UICollectionViewCell() } return cell case Section.title: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TitleCollectionViewCell.className, for: indexPath) as? TitleCollectionViewCell else { return UICollectionViewCell() } + cell.delegate = self return cell case Section.courseList: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } @@ -348,7 +350,7 @@ extension CourseDiscoveryVC: CourseListCVCDeleagte { extension CourseDiscoveryVC { private func getCourseData() { LoadingIndicator.showLoading() - publicCourseProvider.request(.getCourseData(pageNo: pageNo)) { response in + publicCourseProvider.request(.getCourseData(pageNo: pageNo, sort: sort)) { response in LoadingIndicator.hideLoading() switch response { case .success(let result): @@ -379,6 +381,7 @@ extension CourseDiscoveryVC { } } + private func scrapCourse(publicCourseId: Int, scrapTF: Bool) { LoadingIndicator.showLoading() scrapProvider.request(.createAndDeleteScrap(publicCourseId: publicCourseId, scrapTF: scrapTF)) { [weak self] response in @@ -409,3 +412,12 @@ extension CourseDiscoveryVC: ListEmptyViewDelegate { self.tabBarController?.selectedIndex = 0 } } + +extension CourseDiscoveryVC: TitleCollectionViewCellDelegate { + func didTapSortButton(ordering: String) { + // 기존의 getCourseData 함수 호출을 getSortedCourseData로 변경 + pageNo = 1 + sort = ordering + getCourseData() + } +} From f24f5df6d0bf6ee147318fbce780dc3d76c66a87 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 23 Nov 2023 21:17:28 +0900 Subject: [PATCH 11/40] =?UTF-8?q?[Refactor]=20#194=20-=20=EC=85=80=20?= =?UTF-8?q?=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/TitleCollectionViewCell.swift | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index f5e0afb4..2df88303 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -6,12 +6,17 @@ // import UIKit -import SnapKit +import Combine -import Then +protocol TitleCollectionViewCellDelegate: AnyObject { + func didTapSortButton(ordering: String) +} class TitleCollectionViewCell: UICollectionViewCell { + private var cancellables: Set = [] + weak var delegate: TitleCollectionViewCellDelegate? + // MARK: - UI Components private lazy var titleStackView = UIStackView(arrangedSubviews: [mainLabel, subLabel]).then { @@ -42,19 +47,8 @@ class TitleCollectionViewCell: UICollectionViewCell { return label }() - private lazy var dateSort = UIButton(type: .custom).then { - $0.setTitle("최신순", for: .normal) - $0.titleLabel?.font = .b3 - $0.setTitleColor(.m1, for: .normal) - $0.setTitleColor(.g2, for: .disabled) - } - - private lazy var scrapSort = UIButton(type: .custom).then { - $0.setTitle("스크랩순", for: .normal) - $0.titleLabel?.font = .b3 - $0.setTitleColor(.g2, for: .normal) - $0.setTitleColor(.m1, for: .disabled) - } + private lazy var dateSortButton = createSortButton(title: "최신순", ordering: "date") + private lazy var scrapSortButton = createSortButton(title: "스크랩순", ordering: "scrap") // MARK: - Life cycle @@ -68,12 +62,43 @@ class TitleCollectionViewCell: UICollectionViewCell { } } + + +// MARK: - Method + +extension TitleCollectionViewCell { + + private func createSortButton(title: String, ordering: String) -> UIButton { + let button = UIButton(type: .custom).then { + $0.setTitle(title, for: .normal) + $0.titleLabel?.font = .b3 + $0.setTitleColor(.m1, for: .normal) + $0.setTitleColor(.g2, for: .disabled) + } + button.addTarget(self, action: #selector(sortButtonTapped), for: .touchUpInside) + button.tapPublisher + .sink { [weak self] in + guard let self = self else { return } + self.delegate?.didTapSortButton(ordering: ordering) + } + .store(in: &cancellables) + return button + } + + @objc private func sortButtonTapped(sender: UIButton) { + guard let ordering = sender == dateSortButton ? "date" : "scrap" else { + return + } + delegate?.didTapSortButton(ordering: ordering) + } +} + // MARK: - Layout extension TitleCollectionViewCell { func layout() { contentView.backgroundColor = .clear - contentView.addSubviews(titleStackView, divideView, dateSort, scrapSort) + contentView.addSubviews(titleStackView, divideView, dateSortButton, scrapSortButton) titleStackView.snp.makeConstraints { $0.centerY.equalToSuperview() @@ -87,14 +112,14 @@ extension TitleCollectionViewCell { $0.height.equalTo(1) } - dateSort.snp.makeConstraints { + dateSortButton.snp.makeConstraints { $0.top.equalTo(divideView.snp.bottom).offset(54) $0.leading.equalTo(titleStackView.snp.trailing).offset(57) } - scrapSort.snp.makeConstraints { + scrapSortButton.snp.makeConstraints { $0.top.equalTo(divideView.snp.bottom).offset(54) - $0.leading.equalTo(dateSort.snp.trailing).offset(8) + $0.leading.equalTo(dateSortButton.snp.trailing).offset(8) } } } From 63a4045f0227b0fef457501e3aa0378d0211c5af Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 20:35:43 +0900 Subject: [PATCH 12/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=EB=A7=88=EB=9D=BC=ED=86=A4=20DTO=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ResponseDto/MarathonListResponseDto.swift | 31 +++++++++++++++++++ .../Views/VC/CourseDiscoveryVC.swift | 1 - 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/MarathonListResponseDto.swift diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/MarathonListResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/MarathonListResponseDto.swift new file mode 100644 index 00000000..f84dc7f0 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/MarathonListResponseDto.swift @@ -0,0 +1,31 @@ +// +// MarathonListResponseDto.swift +// Runnect-iOS +// +// Created by 이명진 on 11/26/23. +// + +import Foundation + +// MARK: - MarathonListResponseDto + +struct MarathonListResponseDto: Codable { + let marathonPublicCourses: [marathonCourse] +} + +// MARK: - PublicCourse + +struct marathonCourse: Codable { + let id, courseId: Int + let title: String + let image: String + let scrap: Bool? + let departure: Departure +} + +// MARK: - CourseDiscoveryDeparture + +struct Departure: Codable { + let region: String + let city: String +} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 7e60f295..7174699e 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -54,7 +54,6 @@ final class CourseDiscoveryVC: UIViewController { return collectionView }() - // MARK: - View Life Cycle override func viewDidLoad () { From 562cf6cbbf54efa54d1a3bdb7fb4da5106c8ff21 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 20:36:40 +0900 Subject: [PATCH 13/40] =?UTF-8?q?[Feat]=20#194=20-=20=EB=A7=88=EB=9D=BC?= =?UTF-8?q?=ED=86=A4=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20Cell=EC=97=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/MarathonMapCollectionViewCell.swift | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift index 1dbe7995..9ce352d1 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -27,7 +27,7 @@ class MarathonMapCollectionViewCell: UICollectionViewCell { final let collectionViewInset = UIEdgeInsets(top: 0, left: 16, bottom: 34, right: 16) final let itemSpacing: CGFloat = 10 final let lineSpacing: CGFloat = 10 - private var courseList = [PublicCourse]() + private var courseList = [marathonCourse]() // MARK: - Life cycle override init(frame: CGRect) { @@ -35,7 +35,7 @@ class MarathonMapCollectionViewCell: UICollectionViewCell { layout() register() setDelegate() - getCourseData() + getMarathonCourseData() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") @@ -69,7 +69,7 @@ extension MarathonMapCollectionViewCell { } } - func setData(courseList: [PublicCourse]) { + func setData(courseList: [marathonCourse]) { self.courseList = courseList recommendedCollectionView.reloadData() } @@ -117,27 +117,19 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegateFlowLayout { } -// 원래 서버에서 1페이지만 가져온 데이터로 테스트 - extension MarathonMapCollectionViewCell { - private func getCourseData() { + private func getMarathonCourseData() { LoadingIndicator.showLoading() - PublicCourseProvider.request(.getCourseData(pageNo: 1, sort: "date")) { response in + PublicCourseProvider.request(.getMarathonCourseData) { response in LoadingIndicator.hideLoading() switch response { case .success(let result): let status = result.statusCode if 200..<300 ~= status { do { - let responseDto = try result.map(BaseResponse.self) + let responseDto = try result.map(BaseResponse.self) guard let data = responseDto.data else { return } - - // 새로 받은 데이터를 기존 리스트에 추가 (쌓기 위함) - self.courseList.append(contentsOf: data.publicCourses) - - // UI를 업데이트하여 추가된 데이터를 반영합니다. - self.setData(courseList: self.courseList) - + self.setData(courseList: data.marathonPublicCourses) } catch { print(error.localizedDescription) } @@ -150,5 +142,4 @@ extension MarathonMapCollectionViewCell { } } } - } From f22d71365f973af6b8dad6dfea57c325f03ded17 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 20:37:12 +0900 Subject: [PATCH 14/40] =?UTF-8?q?[Feat]=20#194=20-=20=EB=A7=88=EB=9D=BC?= =?UTF-8?q?=ED=86=A4=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj | 4 ++++ .../Runnect-iOS/Network/Router/PublicCourseRouter.swift | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index e6704079..1fff898c 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 712F661D2A7B7BAB00D9539B /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712F661C2A7B7BAB00D9539B /* Config.swift */; }; 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */; }; + 717916DA2B13613B009CEF97 /* MarathonListResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */; }; 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */; }; 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */; }; @@ -180,6 +181,7 @@ 7110A6032AA337DD009A7E99 /* Runnect-iOSDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Runnect-iOSDebug.entitlements"; sourceTree = ""; }; 712F661C2A7B7BAB00D9539B /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; + 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonListResponseDto.swift; sourceTree = ""; }; 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; @@ -1215,6 +1217,7 @@ DAD5A3E0296D4C2A00C8166B /* ResponseDto */ = { isa = PBXGroup; children = ( + 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */, DAD5A3E1296D4C6500C8166B /* PickedMapListResponseDto.swift */, ); path = ResponseDto; @@ -1486,6 +1489,7 @@ A3BC2F4129667A0D00198261 /* NicknameEditorVC.swift in Sources */, CE0C23742966D62A00B45063 /* PagedView.swift in Sources */, CE3A53C5296C6017003D518C /* KeychainManager.swift in Sources */, + 717916DA2B13613B009CEF97 /* MarathonListResponseDto.swift in Sources */, CEEC6B4B2961D89700D00E1E /* CustomNavigationBar.swift in Sources */, CE09037D296E9ED900BEA710 /* ScrapCourseResponseDto.swift in Sources */, CED791B32A2626AF001BFCFB /* ShadowView.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift index 7369d62b..d9137659 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift @@ -11,6 +11,7 @@ import Moya enum PublicCourseRouter { case getCourseData(pageNo: Int, sort: String) case getCourseSearchData(keyword: String) + case getMarathonCourseData case courseUploadingData(param: CourseUploadingRequestDto) case getUploadedCourseDetail(publicCourseId: Int) case getUploadedCourseInfo @@ -31,6 +32,8 @@ extension PublicCourseRouter: TargetType { switch self { case .getCourseData, .courseUploadingData: return "/public-course" + case .getMarathonCourseData: + return "/public-course/marathon" case .getCourseSearchData: return "/public-course/search" case .getUploadedCourseDetail(let publicCourseId): @@ -46,7 +49,7 @@ extension PublicCourseRouter: TargetType { var method: Moya.Method { switch self { - case .getCourseData, .getCourseSearchData, .getUploadedCourseDetail, .getUploadedCourseInfo: + case .getCourseData, .getCourseSearchData, .getMarathonCourseData, .getUploadedCourseDetail, .getUploadedCourseInfo: return .get case .courseUploadingData: return .post @@ -76,7 +79,7 @@ extension PublicCourseRouter: TargetType { fatalError("Encoding 실패")} case .deleteUploadedCourse(let publicCourseIdList): return .requestParameters(parameters: ["publicCourseIdList": publicCourseIdList], encoding: JSONEncoding.default) - case .getUploadedCourseDetail, .getUploadedCourseInfo: + case .getMarathonCourseData, .getUploadedCourseDetail, .getUploadedCourseInfo: return .requestPlain } } From d3780e751d9d3efd2de120c883726370c5054d42 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 21:54:07 +0900 Subject: [PATCH 15/40] =?UTF-8?q?[Feat]=20#194=20-=20Combine=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코스 발견 뷰로 넘겨줄 이벤트를 처리하기 위해 Combine을 사용하였습니다. --- .../Views/MarathonMapCollectionViewCell.swift | 37 +++++++++++++------ .../Views/TitleCollectionViewCell.swift | 1 - 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift index 9ce352d1..3af9021b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -6,11 +6,20 @@ // import UIKit -import SnapKit -import Then +import Combine + + +class CourseSelectionPublisher { + static let shared = CourseSelectionPublisher() + + private init() {} + + let didSelectCourse = PassthroughSubject() +} class MarathonMapCollectionViewCell: UICollectionViewCell { private let PublicCourseProvider = Providers.publicCourseProvider + private var marathonCourseList = [marathonCourse]() private lazy var recommendedCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() @@ -24,12 +33,12 @@ class MarathonMapCollectionViewCell: UICollectionViewCell { // MARK: - Constants - final let collectionViewInset = UIEdgeInsets(top: 0, left: 16, bottom: 34, right: 16) - final let itemSpacing: CGFloat = 10 - final let lineSpacing: CGFloat = 10 - private var courseList = [marathonCourse]() + private let collectionViewInset = UIEdgeInsets(top: 0, left: 16, bottom: 34, right: 16) + private let itemSpacing: CGFloat = 10 + private let lineSpacing: CGFloat = 10 // MARK: - Life cycle + override init(frame: CGRect) { super.init(frame: frame) layout() @@ -62,6 +71,7 @@ extension MarathonMapCollectionViewCell { func layout() { contentView.backgroundColor = .clear contentView.addSubview(recommendedCollectionView) + recommendedCollectionView.snp.makeConstraints { $0.top.equalToSuperview() $0.leading.trailing.equalTo(contentView.safeAreaLayoutGuide) @@ -69,8 +79,8 @@ extension MarathonMapCollectionViewCell { } } - func setData(courseList: [marathonCourse]) { - self.courseList = courseList + func setData(marathonCourseList: [marathonCourse]) { + self.marathonCourseList = marathonCourseList recommendedCollectionView.reloadData() } @@ -79,7 +89,7 @@ extension MarathonMapCollectionViewCell { extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return courseList.count + return marathonCourseList.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -87,7 +97,7 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionV for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .all) - let model = self.courseList[indexPath.item] + let model = self.marathonCourseList[indexPath.item] let location = "\(model.departure.region) \(model.departure.city)" cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scrap, indexPath: indexPath.item) return cell @@ -115,6 +125,11 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegateFlowLayout { return self.lineSpacing } + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + CourseSelectionPublisher.shared.didSelectCourse.send(indexPath) + // 코스 발견에 전달 + } + } extension MarathonMapCollectionViewCell { @@ -129,7 +144,7 @@ extension MarathonMapCollectionViewCell { do { let responseDto = try result.map(BaseResponse.self) guard let data = responseDto.data else { return } - self.setData(courseList: data.marathonPublicCourses) + self.setData(marathonCourseList: data.marathonPublicCourses) } catch { print(error.localizedDescription) } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index 2df88303..8415acd7 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -63,7 +63,6 @@ class TitleCollectionViewCell: UICollectionViewCell { } - // MARK: - Method extension TitleCollectionViewCell { From 16f53cc451a4dcd547ea5aada379e12387e7b5ef Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 21:55:26 +0900 Subject: [PATCH 16/40] =?UTF-8?q?[Feat]=20#194=20-=20Combine=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EB=B0=9B=EB=8A=94=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 코스 발견에 넘어오는 이벤트를 처리하는 코드를 추가하였습니다. --- .../Views/VC/CourseDiscoveryVC.swift | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 7174699e..17caa063 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -6,7 +6,6 @@ // import UIKit - import Then import SnapKit import Combine @@ -19,6 +18,7 @@ final class CourseDiscoveryVC: UIViewController { private let publicCourseProvider = Providers.publicCourseProvider private let scrapProvider = Providers.scrapProvider private var courseList = [PublicCourse]() + private var cancelBag = CancelBag() private var specialList = [String]() private var pageNo = 1 private var sort = "date" @@ -64,6 +64,7 @@ final class CourseDiscoveryVC: UIViewController { setDelegate() layout() setAddTarget() + setCombineEvent() } override func viewWillAppear(_ animated: Bool) { @@ -115,6 +116,14 @@ extension CourseDiscoveryVC { isDataLoaded = true } } + + private func setCombineEvent() { + CourseSelectionPublisher.shared.didSelectCourse + .sink { [weak self] indexPath in + self?.handleCourseSelection(at: indexPath) + } + .store(in: cancelBag) + } } // MARK: - @objc Function @@ -320,7 +329,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - if indexPath.section == Section.recommendedList || indexPath.section == Section.courseList { + if indexPath.section == Section.courseList { let courseDetailVC = CourseDetailVC() let courseModel = courseList[indexPath.item] courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) @@ -328,6 +337,16 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { navigationController?.pushViewController(courseDetailVC, animated: true) } } + + private func handleCourseSelection(at indexPath: IndexPath) { + // 여기서 외부에서 Marathon Cell에서 받아오는 indexPath를 처리 합니다. + // 머지전 주석 삭제 + let courseDetailVC = CourseDetailVC() + let courseModel = courseList[indexPath.item] + courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) + courseDetailVC.hidesBottomBarWhenPushed = true + navigationController?.pushViewController(courseDetailVC, animated: true) + } } // MARK: - CourseListCVCDeleagte From 0009a3fd00f39a62eab40dc686e1795ddf8f7c76 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 21:56:26 +0900 Subject: [PATCH 17/40] =?UTF-8?q?[Refactor]=20#194=20-=20Cyclomatic=20Comp?= =?UTF-8?q?lexity=20=EA=B2=BD=EA=B3=A0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 함수의 코드의 복잡성이 있어, 따로 함수로 빼서 관리해 주게 짰습니다. 추후 저 셀도 따로 빼서 관리할 예정입니다. --- .../Views/VC/CourseDiscoveryVC.swift | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 17caa063..30bdc64e 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -256,13 +256,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc cell.delegate = self return cell case Section.courseList: - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } - cell.setCellType(type: .all) - cell.delegate = self - let model = self.courseList[indexPath.item] - let location = "\(model.departure.region) \(model.departure.city)" - cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scrap, indexPath: indexPath.item) - return cell + return courseListCell(collectionView: collectionView, indexPath: indexPath) default: return UICollectionViewCell() } @@ -289,6 +283,16 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc } } + private func courseListCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } + cell.setCellType(type: .all) + cell.delegate = self + let model = self.courseList[indexPath.item] + let location = "\(model.departure.region) \(model.departure.city)" + cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scrap, indexPath: indexPath.item) + return cell + } + } // MARK: - UICollectionViewDelegateFlowLayout @@ -399,7 +403,6 @@ extension CourseDiscoveryVC { } } - private func scrapCourse(publicCourseId: Int, scrapTF: Bool) { LoadingIndicator.showLoading() scrapProvider.request(.createAndDeleteScrap(publicCourseId: publicCourseId, scrapTF: scrapTF)) { [weak self] response in From 6ae5a941b4e44066cc10699064b9207d86e6e2d6 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 22:41:38 +0900 Subject: [PATCH 18/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EB=9D=BC=EC=9A=B0=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getTotalPageCount 를 추가 하였습니다. --- .../Runnect-iOS/Network/Router/PublicCourseRouter.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift index d9137659..f992f25c 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift @@ -12,6 +12,7 @@ enum PublicCourseRouter { case getCourseData(pageNo: Int, sort: String) case getCourseSearchData(keyword: String) case getMarathonCourseData + case getTotalPageCount case courseUploadingData(param: CourseUploadingRequestDto) case getUploadedCourseDetail(publicCourseId: Int) case getUploadedCourseInfo @@ -36,6 +37,8 @@ extension PublicCourseRouter: TargetType { return "/public-course/marathon" case .getCourseSearchData: return "/public-course/search" + case .getTotalPageCount: + return "public-course/total-page-count" case .getUploadedCourseDetail(let publicCourseId): return "/public-course/detail/\(publicCourseId)" case .getUploadedCourseInfo: @@ -49,7 +52,7 @@ extension PublicCourseRouter: TargetType { var method: Moya.Method { switch self { - case .getCourseData, .getCourseSearchData, .getMarathonCourseData, .getUploadedCourseDetail, .getUploadedCourseInfo: + case .getCourseData, .getCourseSearchData, .getMarathonCourseData, .getUploadedCourseDetail, .getUploadedCourseInfo, .getTotalPageCount: return .get case .courseUploadingData: return .post @@ -79,7 +82,7 @@ extension PublicCourseRouter: TargetType { fatalError("Encoding 실패")} case .deleteUploadedCourse(let publicCourseIdList): return .requestParameters(parameters: ["publicCourseIdList": publicCourseIdList], encoding: JSONEncoding.default) - case .getMarathonCourseData, .getUploadedCourseDetail, .getUploadedCourseInfo: + case .getMarathonCourseData, .getTotalPageCount, .getUploadedCourseDetail, .getUploadedCourseInfo: return .requestPlain } } From ccfad247e7577adb55e6b1d24808b735acc7ee37 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 22:42:37 +0900 Subject: [PATCH 19/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8A=94=20DTO?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 따로 데이터를 받는거라, 하나의 파라미터지만 DTO를 추가하였습니다. --- Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj | 4 ++++ .../ResponseDto/TotalPageCountDto.swift | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/TotalPageCountDto.swift diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 1fff898c..26ae4883 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */; }; 717916DA2B13613B009CEF97 /* MarathonListResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */; }; + 717916DE2B137DC3009CEF97 /* TotalPageCountDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */; }; 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */; }; 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */; }; @@ -182,6 +183,7 @@ 712F661C2A7B7BAB00D9539B /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonListResponseDto.swift; sourceTree = ""; }; + 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalPageCountDto.swift; sourceTree = ""; }; 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; @@ -1219,6 +1221,7 @@ children = ( 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */, DAD5A3E1296D4C6500C8166B /* PickedMapListResponseDto.swift */, + 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */, ); path = ResponseDto; sourceTree = ""; @@ -1462,6 +1465,7 @@ CE1006572968230800FD31FB /* DepartureLocationModel.swift in Sources */, CECA695C296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift in Sources */, CE6655EC295D88D000C64E12 /* UITableView+.swift in Sources */, + 717916DE2B137DC3009CEF97 /* TotalPageCountDto.swift in Sources */, CEEC6B3A2961C4F300D00E1E /* CourseDrawingHomeVC.swift in Sources */, CEB0BCBC29D123350048CCD5 /* GuideView.swift in Sources */, CEC2A6902962B06C00160BF7 /* convertLocationObject.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/TotalPageCountDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/TotalPageCountDto.swift new file mode 100644 index 00000000..f0f1f3e2 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/TotalPageCountDto.swift @@ -0,0 +1,14 @@ +// +// TotalPageCountDto.swift +// Runnect-iOS +// +// Created by 이명진 on 11/26/23. +// + +import Foundation + +// MARK: - TotalPageCountDto + +struct TotalPageCountDto: Codable { + let totalPageCount: Int +} From b7584a34d6eebb56630a1ff2334634ab1412d4fb Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 26 Nov 2023 22:43:30 +0900 Subject: [PATCH 20/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=9C=EC=88=98=20=EB=84=A4=ED=8A=B8=EC=9B=8C=ED=81=AC=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getTotalPageNum() 함수를 추가하여 viewDidLoad 에 추가 해주었습니다. --- .../Views/VC/CourseDiscoveryVC.swift | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 30bdc64e..fd4fd585 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -23,6 +23,7 @@ final class CourseDiscoveryVC: UIViewController { private var pageNo = 1 private var sort = "date" private var isDataLoaded = false + private var totalPageNum = 0 // MARK: - UIComponents @@ -65,6 +66,7 @@ final class CourseDiscoveryVC: UIViewController { layout() setAddTarget() setCombineEvent() + getTotalPageNum() } override func viewWillAppear(_ animated: Bool) { @@ -208,11 +210,11 @@ extension CourseDiscoveryVC { extension CourseDiscoveryVC { private enum Section { - static let adImage = 0 - static let marathonTitle = 1 - static let recommendedList = 2 - static let title = 3 - static let courseList = 4 + static let adImage = 0 // 광고 이미지 + static let marathonTitle = 1 // 마라톤 코스 설명 + static let recommendedList = 2 // 마라톤 코스 + static let title = 3 // 추천 코스 설명 + static let courseList = 4 // 추천 코스 } private enum Layout { @@ -403,6 +405,34 @@ extension CourseDiscoveryVC { } } + private func getTotalPageNum() { + LoadingIndicator.showLoading() + publicCourseProvider.request(.getTotalPageCount) { response in + LoadingIndicator.hideLoading() + switch response { + case .success(let result): + let status = result.statusCode + if 200..<300 ~= status { + do { + let responseDto = try result.map(BaseResponse.self) + guard let data = responseDto.data else { return } + self.totalPageNum = data.totalPageCount + print("추천코스의 코스의 수는 \(self.totalPageNum) 입니다. 🏃‍♀️\n") + } catch { + print(error.localizedDescription) + } + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } + private func scrapCourse(publicCourseId: Int, scrapTF: Bool) { LoadingIndicator.showLoading() scrapProvider.request(.createAndDeleteScrap(publicCourseId: publicCourseId, scrapTF: scrapTF)) { [weak self] response in From 94eef74d445d716d6f181245c601a97b4d52869a Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Wed, 29 Nov 2023 18:44:52 +0900 Subject: [PATCH 21/40] =?UTF-8?q?[Fix]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=EC=A0=95=EB=A0=AC=20=ED=84=B0=EC=B9=98=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=A4=91=EB=B3=B5=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Combine 으로 두번 호출되는 코드를 제거 하였습니다. --- .../CourseDiscovery/Views/TitleCollectionViewCell.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index 8415acd7..c7f7960c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -62,7 +62,6 @@ class TitleCollectionViewCell: UICollectionViewCell { } } - // MARK: - Method extension TitleCollectionViewCell { @@ -74,7 +73,6 @@ extension TitleCollectionViewCell { $0.setTitleColor(.m1, for: .normal) $0.setTitleColor(.g2, for: .disabled) } - button.addTarget(self, action: #selector(sortButtonTapped), for: .touchUpInside) button.tapPublisher .sink { [weak self] in guard let self = self else { return } @@ -82,13 +80,6 @@ extension TitleCollectionViewCell { } .store(in: &cancellables) return button - } - - @objc private func sortButtonTapped(sender: UIButton) { - guard let ordering = sender == dateSortButton ? "date" : "scrap" else { - return - } - delegate?.didTapSortButton(ordering: ordering) } } From 90246bf7d0637de3c6b9d4b353807717922b25c9 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Wed, 29 Nov 2023 18:48:05 +0900 Subject: [PATCH 22/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=EC=84=9C=EB=B2=84=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EC=A3=BC=EB=8A=94=20=EA=B0=AF=EC=88=98=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDiscovery/Views/VC/CourseDiscoveryVC.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index fd4fd585..da5d6dcc 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -17,6 +17,7 @@ final class CourseDiscoveryVC: UIViewController { private let publicCourseProvider = Providers.publicCourseProvider private let scrapProvider = Providers.scrapProvider + private let serverResponseNumber = 24 private var courseList = [PublicCourse]() private var cancelBag = CancelBag() private var specialList = [String]() From b22980f9a36b11ac2a665ea81a1447a3d446b5ea Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Wed, 29 Nov 2023 18:56:45 +0900 Subject: [PATCH 23/40] =?UTF-8?q?[Feat]=20#194=20-=20pagination=20?= =?UTF-8?q?=EC=A2=80=20=EB=8D=94=20=EB=B6=80=EB=93=9C=EB=9F=BD=EA=B2=8C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 원래 빨리 내리거나, 너무 쭉 내리면 page 수가 너무 빨리 올라가 페이징이 의미가 없는 구현이 됐던 코드 입니다. 동기처리를 추가 하였고, 일부 pagination 로직 코드 수정을 하였습니다. 동기 처리를 await, task.sleep으로 가능하면 그걸로 할 예정 --- .../Views/VC/CourseDiscoveryVC.swift | 112 ++++++++++-------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index da5d6dcc..86cb0f1c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -18,6 +18,7 @@ final class CourseDiscoveryVC: UIViewController { private let publicCourseProvider = Providers.publicCourseProvider private let scrapProvider = Providers.scrapProvider private let serverResponseNumber = 24 + private var courseList = [PublicCourse]() private var cancelBag = CancelBag() private var specialList = [String]() @@ -43,6 +44,9 @@ final class CourseDiscoveryVC: UIViewController { private let emptyView = ListEmptyView(description: "공유할 수 있는 코스가 없어요!\n코스를 그려주세요", buttonTitle: "코스 그리기") + private let refreshControl = UIRefreshControl() + // refreshControl 사용시 사용 + // 마지막 머지에 사용안하면 삭제 // MARK: - collectionview @@ -67,13 +71,15 @@ final class CourseDiscoveryVC: UIViewController { layout() setAddTarget() setCombineEvent() - getTotalPageNum() + // getTotalPageNum() + self.getCourseData() + self.totalPageNum = 5 // test 코드 } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.hideTabBar(wantsToHide: false) - setDataLoadIfNeeded() + // 여기에 기존 데이터 한번에 불러오는 코스 작성 page 순서만 asyc로 작업 } } @@ -110,15 +116,15 @@ extension CourseDiscoveryVC { self.uploadButton.addTarget(self, action: #selector(pushToDiscoveryVC), for: .touchUpInside) } - private func setDataLoadIfNeeded() { /// 데이터를 받고 다른 뷰를 갔다가 와도 데이터가 유지되게끔 하기 위한 함수 입니다. (한번만 호출되면 되는 함수!) - if !isDataLoaded { - courseList.removeAll() - pageNo = 1 - mapCollectionView.reloadData() - getCourseData() - isDataLoaded = true - } - } + // private func setDataLoadIfNeeded() { /// 데이터를 받고 다른 뷰를 갔다가 와도 데이터가 유지되게끔 하기 위한 함수 입니다. (한번만 호출되면 되는 함수!) + // if !isDataLoaded { + // courseList.removeAll() + // pageNo = 1 + // mapCollectionView.reloadData() + // getCourseData() + // isDataLoaded = true + // } + // } private func setCombineEvent() { CourseSelectionPublisher.shared.didSelectCourse @@ -266,23 +272,26 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc } func scrollViewDidScroll(_ scrollView: UIScrollView) { - let contentOffsetY = scrollView.contentOffset.y - let collectionViewHeight = mapCollectionView.contentSize.height - let paginationY = collectionViewHeight * 0.2 + let contentOffsetY = mapCollectionView.contentOffset.y // 우리가 보는 화면 + let collectionViewHeight = mapCollectionView.contentSize.height // 전체 사이즈 + let paginationY = mapCollectionView.bounds.size.height // 유저 화면의 가장 아래 y축 이라고 생각 - // 스크롤이 80% (0.2) 까지 도달하면 다음 페이지 데이터를 불러옵니다. - if contentOffsetY >= collectionViewHeight - paginationY { - if courseList.count < pageNo * 24 { // 페이지 끝에 도달하면 현재 페이지에 더 이상 데이터가 없음을 의미합니다. + if contentOffsetY > collectionViewHeight - paginationY { + if courseList.count < pageNo * serverResponseNumber { + // 페이지 끝에 도달하면 현재 페이지에 더 이상 데이터가 없음을 의미 + // 새로온 데이터의 갯수가 원래 서버에서 응답에서 온 갯수보다 작으면 페이지네이션 금지 // 페이지네이션 중단 코드 return } - - // 다음 페이지 번호를 증가시킵니다. - pageNo += 1 - print("🔥다음 페이지 로드: \(pageNo)🔥") - - // 여기에서 다음 페이지 데이터를 불러오는 함수를 호출하세요. - getCourseData() + print("🫠\(pageNo)") + if pageNo < totalPageNum { + if !isDataLoaded { + isDataLoaded = true + getCourseData() + pageNo += 1 + isDataLoaded = false + } + } } } @@ -374,34 +383,37 @@ extension CourseDiscoveryVC: CourseListCVCDeleagte { extension CourseDiscoveryVC { private func getCourseData() { - LoadingIndicator.showLoading() - publicCourseProvider.request(.getCourseData(pageNo: pageNo, sort: sort)) { response in - LoadingIndicator.hideLoading() - switch response { - case .success(let result): - let status = result.statusCode - if 200..<300 ~= status { - do { - let responseDto = try result.map(BaseResponse.self) - guard let data = responseDto.data else { return } - - // 새로 받은 데이터를 기존 리스트에 추가 (쌓기 위함) - self.courseList.append(contentsOf: data.publicCourses) - - // UI를 업데이트하여 추가된 데이터를 반영합니다. - self.mapCollectionView.reloadData() - - } catch { - print(error.localizedDescription) + LoadingIndicator.showLoading() // 항상 0.7초 늦게 로딩이 되어 버림 0.7초를 넣은 이유는 pagination을 구현할때 한번에 다 받아오지 않게 하기 위함 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { [self] in + publicCourseProvider.request(.getCourseData(pageNo: pageNo, sort: sort)) { response in + LoadingIndicator.hideLoading() + print("‼️ 이번 sort 는 요?? \(self.sort) ‼️\n") + switch response { + case .success(let result): + let status = result.statusCode + + if 200..<300 ~= status { + do { + let responseDto = try result.map(BaseResponse.self) + guard let data = responseDto.data else { return } + + self.courseList.append(contentsOf: data.publicCourses) + self.mapCollectionView.reloadData() + + } catch { + print(error.localizedDescription) + } } - } - if status >= 400 { - print("400 error") + + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + + case .failure(let error): + print(error.localizedDescription) self.showNetworkFailureToast() } - case .failure(let error): - print(error.localizedDescription) - self.showNetworkFailureToast() } } } @@ -469,7 +481,9 @@ extension CourseDiscoveryVC: TitleCollectionViewCellDelegate { func didTapSortButton(ordering: String) { // 기존의 getCourseData 함수 호출을 getSortedCourseData로 변경 pageNo = 1 + print("‼️\(ordering)‼️ 터치 하셨습니다. 0.7초 후에 ‼️\(ordering)‼️ 으로 정렬이 되는 데이터가 불러 옵니다.") sort = ordering + self.courseList.removeAll() getCourseData() } } From a67cbe38625725305962afba174371b8f76905e5 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Wed, 29 Nov 2023 19:04:53 +0900 Subject: [PATCH 24/40] =?UTF-8?q?[Chore]=20#194=20-=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 디버깅용 주석은 냅뒀습니다~ --- .../Global/Utils/RNUtils/UserManager.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift index 31570cb8..a6e58f70 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/UserManager.swift @@ -53,7 +53,6 @@ final class UserManager { self.isKakao = provider == "KAKAO" ? true : false UserManager.shared.userType = .registered completion(.success(data.type)) // 로그인인지 회원가입인지 전달 - print("\n\n 🥰\(String(describing: data.email))\n\n") } catch { print(error.localizedDescription) completion(.failure(.networkFail)) @@ -67,7 +66,7 @@ final class UserManager { print(error.localizedDescription) if let response = error.response { if let responseData = String(data: response.data, encoding: .utf8) { - print("\n\n SignIn 메세지 ‼️🔥\(responseData)\n\n") + print("\n 🔥 SignIn 메세지 \(responseData)\n") // 이 코드는 2차 업데이트 토큰 부분 디버깅 용으로 사용합니다. (업데이트 이후 제거) } else { print(error.localizedDescription) } @@ -103,17 +102,18 @@ final class UserManager { completion(.failure(.networkFail)) } case .failure(let error): - if let response = error.response { - if let responseData = String(data: response.data, encoding: .utf8) { - print("\n\n getNewToken 메세지 ‼️🔥\(responseData)\n\n") - } else { - print(error.localizedDescription) - } - } else { - print(error.localizedDescription) - } - print(error.localizedDescription) + // 아래 코드는 2차 업데이트 토큰 부분 디버깅 용으로 사용합니다. (업데이트 이후 제거) +// if let response = error.response { +// if let responseData = String(data: response.data, encoding: .utf8) { +// print("\n getNewToken 메세지 ‼️🔥\(responseData)\n") +// } else { +// print(error.localizedDescription) +// } +// } else { +// print(error.localizedDescription) +// } + completion(.failure(.networkFail)) } } From 553953909706736471800d537b26fbbab00d19a5 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Wed, 29 Nov 2023 19:15:07 +0900 Subject: [PATCH 25/40] =?UTF-8?q?[Chore]=20#194=20-=20RecommendedListCVC?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MarathonCourseListCVC 마라톤 코스로 네이밍 변경 하였습니다. --- .../Runnect-iOS.xcodeproj/project.pbxproj | 8 ++++---- .../Views/MarathonMapCollectionViewCell.swift | 9 ++++----- ...stCVC.swift => MarathonCourseListCVC.swift} | 18 +++++++++--------- 3 files changed, 17 insertions(+), 18 deletions(-) rename Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/{RecommendedListCVC.swift => MarathonCourseListCVC.swift} (93%) diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 26ae4883..4831ff92 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ 717916DE2B137DC3009CEF97 /* TotalPageCountDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */; }; 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; 71F780502B0893D700B53253 /* MarathonMapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */; }; - 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */; }; + 71F7BF072B0CDFE300B752B3 /* MarathonCourseListCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7BF062B0CDFE300B752B3 /* MarathonCourseListCVC.swift */; }; A3305A97296EF58C000B1A10 /* GoalRewardInfoDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */; }; A3BC2F2B2962C3D500198261 /* GoalRewardInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */; }; A3BC2F2D2962C3F200198261 /* ActivityRecordInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */; }; @@ -187,7 +187,7 @@ 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; - 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedListCVC.swift; sourceTree = ""; }; + 71F7BF062B0CDFE300B752B3 /* MarathonCourseListCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonCourseListCVC.swift; sourceTree = ""; }; A3305A96296EF58C000B1A10 /* GoalRewardInfoDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoDto.swift; sourceTree = ""; }; A3BC2F2A2962C3D500198261 /* GoalRewardInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalRewardInfoVC.swift; sourceTree = ""; }; A3BC2F2C2962C3F200198261 /* ActivityRecordInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityRecordInfoVC.swift; sourceTree = ""; }; @@ -1168,7 +1168,7 @@ isa = PBXGroup; children = ( CE6B63D2296725E6003F900F /* CourseListCVC.swift */, - 71F7BF062B0CDFE300B752B3 /* RecommendedListCVC.swift */, + 71F7BF062B0CDFE300B752B3 /* MarathonCourseListCVC.swift */, ); path = CVC; sourceTree = ""; @@ -1460,7 +1460,7 @@ A3D1A77E29CF09B600DD54EC /* SignInResponseDto.swift in Sources */, CE21C028299E5FFC00F62AF5 /* PublicCourseRouter.swift in Sources */, CE18E894296C79B900FEB569 /* CourseDrawingRequestDto.swift in Sources */, - 71F7BF072B0CDFE300B752B3 /* RecommendedListCVC.swift in Sources */, + 71F7BF072B0CDFE300B752B3 /* MarathonCourseListCVC.swift in Sources */, DA20D84E2966A9B300F1581F /* CourseSearchVC.swift in Sources */, CE1006572968230800FD31FB /* DepartureLocationModel.swift in Sources */, CECA695C296E61D6002AF05C /* PrivateCourseNotUploadedResponseDto.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift index 3af9021b..bb85350b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -8,7 +8,6 @@ import UIKit import Combine - class CourseSelectionPublisher { static let shared = CourseSelectionPublisher() @@ -58,8 +57,8 @@ extension MarathonMapCollectionViewCell { recommendedCollectionView.dataSource = self } private func register() { - recommendedCollectionView.register(CourseListCVC.self, - forCellWithReuseIdentifier: CourseListCVC.className) + recommendedCollectionView.register(MarathonCourseListCVC.self, + forCellWithReuseIdentifier: MarathonCourseListCVC.className) } } // MARK: - Extensions @@ -93,9 +92,9 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionV } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonCourseListCVC.className, for: indexPath) - as? CourseListCVC else { return UICollectionViewCell() } + as? MarathonCourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .all) let model = self.marathonCourseList[indexPath.item] let location = "\(model.departure.region) \(model.departure.city)" diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/MarathonCourseListCVC.swift similarity index 93% rename from Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift rename to Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/MarathonCourseListCVC.swift index 9e783eee..97981b83 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/RecommendedListCVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CVC/MarathonCourseListCVC.swift @@ -1,5 +1,5 @@ // -// RecommendedListCVC.swift +// MarathonCourseListCVC.swift // Runnect-iOS // // Created by 이명진 on 11/21/23. @@ -7,12 +7,12 @@ import UIKit -protocol RecommendedListCVCDeleagte: AnyObject { +protocol MarathonCourseListCVCDeleagte: AnyObject { func likeButtonTapped(wantsTolike: Bool, index: Int) } @frozen -enum RecommendedListCVCType { +enum MarathonCourseListCVCType { case title case titleWithLocation case all @@ -28,11 +28,11 @@ enum RecommendedListCVCType { } } -final class RecommendedListCVC: UICollectionViewCell { +final class MarathonCourseListCVC: UICollectionViewCell { // MARK: - Properties - weak var delegate: RecommendedListCVCDeleagte? + weak var delegate: MarathonCourseListCVCDeleagte? private var indexPath: Int? @@ -100,7 +100,7 @@ final class RecommendedListCVC: UICollectionViewCell { // MARK: - Methods -extension RecommendedListCVC { +extension MarathonCourseListCVC { private func setAddTarget() { likeButton.addTarget(self, action: #selector(likeButtonDidTap), for: .touchUpInside) } @@ -135,7 +135,7 @@ extension RecommendedListCVC { // MARK: - @objc Function -extension RecommendedListCVC { +extension MarathonCourseListCVC { @objc func likeButtonDidTap(_ sender: UIButton) { guard let indexPath = self.indexPath else { return } if UserManager.shared.userType != .visitor { @@ -147,7 +147,7 @@ extension RecommendedListCVC { // MARK: - UI & Layout -extension RecommendedListCVC { +extension MarathonCourseListCVC { private func setUI() { self.contentView.backgroundColor = .w1 } @@ -186,7 +186,7 @@ extension RecommendedListCVC { } } - func setCellType(type: RecommendedListCVCType) { + func setCellType(type: MarathonCourseListCVCType) { switch type { case .title: self.locationLabel.isHidden = true From 2bacc00d0299fac970b2fa688558c05bbda41b12 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Wed, 29 Nov 2023 19:36:09 +0900 Subject: [PATCH 26/40] =?UTF-8?q?[Chore]=20#194=20-=20=EC=A6=89=EC=8B=9C?= =?UTF-8?q?=20=EC=8B=A4=ED=96=89=20=ED=81=B4=EB=A1=9C=EC=A0=80=20->=20Then?= =?UTF-8?q?=20=EB=AC=B8=EB=B2=95=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MarathonTitleCollectionViewCell.swift | 25 ++++++++--------- .../Views/TitleCollectionViewCell.swift | 28 ++++++++----------- .../Views/VC/CourseDiscoveryVC.swift | 2 +- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift index 4fae3cee..9a52b41b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift @@ -17,20 +17,17 @@ class MarathonTitleCollectionViewCell: UICollectionViewCell { $0.alignment = .leading } - private let mainLabel: UILabel = { - let label = UILabel() - label.text = "2023 마라톤 코스" - label.font = UIFont.h3 - label.textColor = UIColor.g1 - return label - }() - private let subLabel: UILabel = { - let label = UILabel() - label.text = "실제 마라톤 코스를 만나보세요" - label.font = UIFont.b6 - label.textColor = UIColor.g2 - return label - }() + private let mainLabel: UILabel = UILabel().then { + $0.text = "2023 마라톤 코스" + $0.font = UIFont.h3 + $0.textColor = UIColor.g1 + } + + private let subLabel: UILabel = UILabel().then { + $0.text = "실제 마라톤 코스를 만나보세요" + $0.font = UIFont.b6 + $0.textColor = UIColor.g2 + } // MARK: - Life cycle diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index c7f7960c..07921a1b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -29,23 +29,17 @@ class TitleCollectionViewCell: UICollectionViewCell { $0.backgroundColor = .g4 } - private let mainLabel: UILabel = { - let label = UILabel().then { - $0.text = "이런 코스 어때요?" - $0.font = UIFont.h3 - $0.textColor = UIColor.g1 - } - return label - }() - - private let subLabel: UILabel = { - let label = UILabel().then { - $0.text = "나에게 최적화된 코스를 찾아보세요" - $0.font = UIFont.b6 - $0.textColor = UIColor.g2 - } - return label - }() + private let mainLabel: UILabel = UILabel().then { + $0.text = "이런 코스 어때요?" + $0.font = UIFont.h3 + $0.textColor = UIColor.g1 + } + + private let subLabel: UILabel = UILabel().then { + $0.text = "나에게 최적화된 코스를 찾아보세요" + $0.font = UIFont.b6 + $0.textColor = UIColor.g2 + } private lazy var dateSortButton = createSortButton(title: "최신순", ordering: "date") private lazy var scrapSortButton = createSortButton(title: "스크랩순", ordering: "scrap") diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 86cb0f1c..6a29266e 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -430,7 +430,7 @@ extension CourseDiscoveryVC { let responseDto = try result.map(BaseResponse.self) guard let data = responseDto.data else { return } self.totalPageNum = data.totalPageCount - print("추천코스의 코스의 수는 \(self.totalPageNum) 입니다. 🏃‍♀️\n") + print("추천 코스의 코스의 수는 \(self.totalPageNum) 입니다. 🏃‍♀️\n") } catch { print(error.localizedDescription) } From e69ef01824d611fbc0644113a9b54e7ff9aebfa1 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 30 Nov 2023 19:51:18 +0900 Subject: [PATCH 27/40] =?UTF-8?q?[Fix]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8A=94=20=EA=B0=AF=EC=88=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDiscovery/Views/VC/CourseDiscoveryVC.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 6a29266e..b8598539 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -17,7 +17,7 @@ final class CourseDiscoveryVC: UIViewController { private let publicCourseProvider = Providers.publicCourseProvider private let scrapProvider = Providers.scrapProvider - private let serverResponseNumber = 24 + private let serverResponseNumber = 10 private var courseList = [PublicCourse]() private var cancelBag = CancelBag() From 3a44e5abef528215eacffb1389d5ebb8368c0298 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 30 Nov 2023 20:27:03 +0900 Subject: [PATCH 28/40] =?UTF-8?q?[Fix]=20#194=20-=20level=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=ED=98=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ResponseDto/UploadedCourseDetailResponseDto.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift index 9d72ad5c..0c1de183 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift @@ -18,7 +18,7 @@ struct UploadedCourseDetailResponseDto: Codable { struct UploadUser: Codable { let nickname: String - let level: String + let level: Int let image: String let isNowUser: Bool? } From f335617a98742ba4c894bf74b8910e046669b475 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Fri, 1 Dec 2023 19:07:10 +0900 Subject: [PATCH 29/40] =?UTF-8?q?[Chore]=20#194=20-=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDiscovery/Views/VC/CourseSearchVC.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift index 1e5f66b9..ac5879b2 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift @@ -209,8 +209,8 @@ extension CourseSearchVC: CourseListCVCDeleagte { return } - let pubilcCourseId = courseList[index].id - scrapCourse(publicCourseId: pubilcCourseId, scrapTF: wantsTolike) + let publicCourseId = courseList[index].id + scrapCourse(publicCourseId: publicCourseId, scrapTF: wantsTolike) } } From 841a946264843a74574848703b0e1b1f8b37e703 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 5 Dec 2023 01:45:30 +0900 Subject: [PATCH 30/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=201=EC=B0=A8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 함수명 명명규칙 set ~~ 으로 변경 MarathonMapCVC 파일에서 "MarathonCourseListCVC" 를 "CourseListCVC" 으로 변경 --- .../Views/AdImageCollectionViewCell.swift | 28 ++++++------- .../Views/MapCollectionViewCell.swift | 19 ++++----- .../Views/MarathonMapCollectionViewCell.swift | 39 +++++++++++-------- .../MarathonTitleCollectionViewCell.swift | 11 ++---- .../Views/TitleCollectionViewCell.swift | 32 +++++++-------- .../Views/VC/CourseDiscoveryVC.swift | 4 +- 6 files changed, 68 insertions(+), 65 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift index 3d28022b..97142a43 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift @@ -29,7 +29,7 @@ class AdImageCollectionViewCell: UICollectionViewCell, UIScrollViewDelegate { // MARK: - Constants final let collectionViewInset = UIEdgeInsets(top: 28, left: 16, bottom: 28, right: 16) - + // MARK: - UI Components var imgBanners: [UIImage] = [ImageLiterals.imgBanner1, ImageLiterals.imgBanner2] var currentPage: Int = 0 @@ -41,7 +41,7 @@ class AdImageCollectionViewCell: UICollectionViewCell, UIScrollViewDelegate { override init(frame: CGRect) { super.init(frame: frame) - layout() + setLayout() setDelegate() startBannerSlide() } @@ -81,10 +81,10 @@ extension AdImageCollectionViewCell { let currentPage = Int(scrollView.contentOffset.x / scrollView.frame.width) pageControl.currentPage = currentPage % imgBanners.count } - + // MARK: - Layout Helpers - func layout() { + private func setLayout() { contentView.backgroundColor = .clear contentView.addSubview(bannerCollectionView) contentView.addSubview(pageControl) @@ -129,18 +129,18 @@ extension AdImageCollectionViewCell: UICollectionViewDelegate, UICollectionViewD cell.contentView.addSubviews(imageView) if indexPath.item == 0 { - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(firstCellTapped(_:))) - imageView.addGestureRecognizer(tapGesture) - } + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(firstCellTapped(_:))) + imageView.addGestureRecognizer(tapGesture) + } return cell } // 첫 번째 셀 클릭 이벤트 핸들러 - @objc func firstCellTapped(_ gesture: UITapGestureRecognizer) { - // Safari 링크로 연결 - if let url = URL(string: "https://docs.google.com/forms/d/1cpgZHNNi1kIvi2ZCwCIcMJcI1PkHBz9a5vWJb7FfIbg/edit") { - UIApplication.shared.open(url) - } - } + @objc func firstCellTapped(_ gesture: UITapGestureRecognizer) { + // Safari 링크로 연결 + if let url = URL(string: "https://docs.google.com/forms/d/1cpgZHNNi1kIvi2ZCwCIcMJcI1PkHBz9a5vWJb7FfIbg/edit") { + UIApplication.shared.open(url) + } + } } @@ -150,7 +150,7 @@ extension AdImageCollectionViewCell: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return self.frame.size } - + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 0 } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift index 13d35d1b..13a7fb7b 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift @@ -15,13 +15,13 @@ import SnapKit import Then class MapCollectionViewCell: UICollectionViewCell { - + // MARK: - collectionview private lazy var mapCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical - + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .clear collectionView.translatesAutoresizingMaskIntoConstraints = false @@ -35,11 +35,11 @@ class MapCollectionViewCell: UICollectionViewCell { final let collectionViewInset = UIEdgeInsets(top: 28, left: 16, bottom: 28, right: 16) final let itemSpacing: CGFloat = 10 final let lineSpacing: CGFloat = 20 - + // MARK: - Life cycle override init(frame: CGRect) { super.init(frame: frame) - layout() + setLayout() register() setDelegate() } @@ -56,25 +56,26 @@ extension MapCollectionViewCell { } private func register() { mapCollectionView.register(CourseListCVC.self, - forCellWithReuseIdentifier: CourseListCVC.className) + forCellWithReuseIdentifier: CourseListCVC.className) } } -// MARK: - Extensions extension MapCollectionViewCell { // MARK: - Layout Helpers - func layout() { + private func setLayout() { contentView.backgroundColor = .clear contentView.addSubview(mapCollectionView) mapCollectionView.snp.makeConstraints { $0.top.equalToSuperview() $0.leading.trailing.equalTo(contentView.safeAreaLayoutGuide) $0.bottom.equalToSuperview() -// $0.height.equalTo(1000) + // $0.height.equalTo(1000) } } + + } // MARK: - UICollectionViewDelegate, UICollectionViewDataSource @@ -113,5 +114,5 @@ extension MapCollectionViewCell: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return self.lineSpacing } - + } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift index bb85350b..03d718a9 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -20,7 +20,7 @@ class MarathonMapCollectionViewCell: UICollectionViewCell { private let PublicCourseProvider = Providers.publicCourseProvider private var marathonCourseList = [marathonCourse]() - private lazy var recommendedCollectionView: UICollectionView = { + private lazy var marathonCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) @@ -40,7 +40,7 @@ class MarathonMapCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) - layout() + setLayout() register() setDelegate() getMarathonCourseData() @@ -50,41 +50,43 @@ class MarathonMapCollectionViewCell: UICollectionViewCell { } } + + // MARK: - Method + extension MarathonMapCollectionViewCell { private func setDelegate() { - recommendedCollectionView.delegate = self - recommendedCollectionView.dataSource = self + marathonCollectionView.delegate = self + marathonCollectionView.dataSource = self } private func register() { - recommendedCollectionView.register(MarathonCourseListCVC.self, - forCellWithReuseIdentifier: MarathonCourseListCVC.className) + marathonCollectionView.register(CourseListCVC.self, + forCellWithReuseIdentifier: CourseListCVC.className) } } -// MARK: - Extensions extension MarathonMapCollectionViewCell { // MARK: - Layout Helpers - func layout() { + private func setLayout() { contentView.backgroundColor = .clear - contentView.addSubview(recommendedCollectionView) + contentView.addSubview(marathonCollectionView) - recommendedCollectionView.snp.makeConstraints { + marathonCollectionView.snp.makeConstraints { $0.top.equalToSuperview() $0.leading.trailing.equalTo(contentView.safeAreaLayoutGuide) $0.bottom.equalToSuperview() } } - func setData(marathonCourseList: [marathonCourse]) { + private func setData(marathonCourseList: [marathonCourse]) { self.marathonCourseList = marathonCourseList - recommendedCollectionView.reloadData() + marathonCollectionView.reloadData() } } -// MARK: - UICollectionViewDelegate, UICollectionViewDataSource + // MARK: - UICollectionViewDelegate, UICollectionViewDataSource extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { @@ -92,9 +94,9 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionV } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MarathonCourseListCVC.className, + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) - as? MarathonCourseListCVC else { return UICollectionViewCell() } + as? CourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .all) let model = self.marathonCourseList[indexPath.item] let location = "\(model.departure.region) \(model.departure.city)" @@ -103,7 +105,7 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegate, UICollectionV } } -// MARK: - UICollectionViewDelegateFlowLayout + // MARK: - UICollectionViewDelegateFlowLayout extension MarathonMapCollectionViewCell: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { @@ -126,11 +128,14 @@ extension MarathonMapCollectionViewCell: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { CourseSelectionPublisher.shared.didSelectCourse.send(indexPath) - // 코스 발견에 전달 + // 코스 발견에 이벤트 전달 } } + + // MARK: - NetWork + extension MarathonMapCollectionViewCell { private func getMarathonCourseData() { LoadingIndicator.showLoading() diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift index 9a52b41b..7ba7c535 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonTitleCollectionViewCell.swift @@ -22,7 +22,7 @@ class MarathonTitleCollectionViewCell: UICollectionViewCell { $0.font = UIFont.h3 $0.textColor = UIColor.g1 } - + private let subLabel: UILabel = UILabel().then { $0.text = "실제 마라톤 코스를 만나보세요" $0.font = UIFont.b6 @@ -33,20 +33,17 @@ class MarathonTitleCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) - layout() + setLayout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } -// MARK: - Extensions + // MARK: - Layout Helpers extension MarathonTitleCollectionViewCell { - - // MARK: - Layout Helpers - - func layout() { + private func setLayout() { contentView.backgroundColor = .clear contentView.addSubview(titleStackView) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index 07921a1b..57087151 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -16,7 +16,7 @@ class TitleCollectionViewCell: UICollectionViewCell { private var cancellables: Set = [] weak var delegate: TitleCollectionViewCellDelegate? - + // MARK: - UI Components private lazy var titleStackView = UIStackView(arrangedSubviews: [mainLabel, subLabel]).then { @@ -34,7 +34,7 @@ class TitleCollectionViewCell: UICollectionViewCell { $0.font = UIFont.h3 $0.textColor = UIColor.g1 } - + private let subLabel: UILabel = UILabel().then { $0.text = "나에게 최적화된 코스를 찾아보세요" $0.font = UIFont.b6 @@ -48,7 +48,7 @@ class TitleCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) - layout() + setLayout() } required init?(coder: NSCoder) { @@ -61,26 +61,26 @@ class TitleCollectionViewCell: UICollectionViewCell { extension TitleCollectionViewCell { private func createSortButton(title: String, ordering: String) -> UIButton { - let button = UIButton(type: .custom).then { - $0.setTitle(title, for: .normal) - $0.titleLabel?.font = .b3 - $0.setTitleColor(.m1, for: .normal) - $0.setTitleColor(.g2, for: .disabled) + let button = UIButton(type: .custom).then { + $0.setTitle(title, for: .normal) + $0.titleLabel?.font = .b3 + $0.setTitleColor(.m1, for: .normal) + $0.setTitleColor(.g2, for: .disabled) + } + button.tapPublisher + .sink { [weak self] in + guard let self = self else { return } + self.delegate?.didTapSortButton(ordering: ordering) } - button.tapPublisher - .sink { [weak self] in - guard let self = self else { return } - self.delegate?.didTapSortButton(ordering: ordering) - } - .store(in: &cancellables) - return button + .store(in: &cancellables) + return button } } // MARK: - Layout extension TitleCollectionViewCell { - func layout() { + private func setLayout() { contentView.backgroundColor = .clear contentView.addSubviews(titleStackView, divideView, dateSortButton, scrapSortButton) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index b8598539..977ee95e 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -68,7 +68,7 @@ final class CourseDiscoveryVC: UIViewController { register() setNavigationBar() setDelegate() - layout() + setLayout() setAddTarget() setCombineEvent() // getTotalPageNum() @@ -177,7 +177,7 @@ extension CourseDiscoveryVC { } } - private func layout() { + private func setLayout() { view.addSubviews(uploadButton, mapCollectionView) view.bringSubviewToFront(uploadButton) mapCollectionView.addSubview(emptyView) From 98ce908c75234e2eff6647e0a7c20b011e61e1f9 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 5 Dec 2023 21:08:25 +0900 Subject: [PATCH 31/40] =?UTF-8?q?[Fix]=20#194=20-=20Assets=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구 버전 ic_plus_button 을 새 버전으로 변경 하였습니다. --- .../ic_plus_button.imageset/Component 125.png | Bin 0 -> 932 bytes .../Component 125@2x.png | Bin 0 -> 1642 bytes .../Component 125@3x.png | Bin 0 -> 2434 bytes .../ic_plus_button.imageset/Contents.json | 6 +++--- .../ic_plus_button.imageset/plus.png | Bin 3910 -> 0 bytes .../ic_plus_button.imageset/plus@2x.png | Bin 10591 -> 0 bytes .../ic_plus_button.imageset/plus@3x.png | Bin 19720 -> 0 bytes 7 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/Component 125.png create mode 100644 Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/Component 125@2x.png create mode 100644 Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/Component 125@3x.png delete mode 100644 Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus.png delete mode 100644 Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus@2x.png delete mode 100644 Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus@3x.png diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/Component 125.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/Component 125.png new file mode 100644 index 0000000000000000000000000000000000000000..3977ac9109031669c6e8f977a78ac4a7f41b9dd0 GIT binary patch literal 932 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

HSD1_EsbK?XzfUy=7cvb(b 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 U%FLDrO#lD@07*qoM6N<$f-g6Y761SM diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus@2x.png deleted file mode 100644 index 0e2ac0c8d0b44c069097e714e0ed72bb88d7147f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10591 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 diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_plus_button.imageset/plus@3x.png deleted file mode 100644 index 5462443f15aeeaad9525178f56901bef21461988..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19720 zcmV*DKy1H>P)^j{&)7{h4v;Xg_uBu&i zs;aB@+UvbGQ9(gLK|w)5K|w)5K|#U(g=&I*+g>$IQ`vLEzF)WfzF?M7Q^Bl4h@diy zf(hrs3ooqAIefk&s74lloeGe(UXKd)F3LzZ!_ZEyonG5yKb|JXn%p_E9go@bruHZe zh_5lvWUm*W&+HfMAqoeZ1{e<0wyjbb+HqA+WCFM83CZYyCq0?ctT-ZzBRCTUxFXlLJ3jx`sW7^|&+%@gBBQe^0J1%Dl zFw2$wardsQOMwg_7VJ;39e}9MV3BRhiGq~ZdvY9zJ9vgjvWuKIjKypZJ?mM|;x<_i z*=^G{WcU3&2XRU-YErviH&9e2!_}QWM^w1n{D&3kn$(G#BdVOv;t9&@P1=#Q(??FC z1f#?h`VD+Yq*p?7cjzGe)5Co#zZ41tk^Wu6E$35Y;alM4#l*^g_ujAi`z9%a# z+jmL>#74?+v%=B#5jd{YD!Mp>g&l?alz(F|h@DWyu^fkzB}YW9>#!`6?M1TWh=<#c zIN}I?OSK7zx38&@T#yIF%thT!^ix6G0=ezZvw1#=W$zV9O} =_DRe!Q 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 blf(Z9+LHW^b%ym!00000NkvXXu0mjfiu^Hu From b695b8b7814bec250728b1b51c0409cde7993245 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 5 Dec 2023 21:54:00 +0900 Subject: [PATCH 32/40] =?UTF-8?q?[Refactor]=20#194=20-=20pagination=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UIScrollViewDelegate 로 따로 extension을 분리 해주었습니다. --- .../Views/VC/CourseDiscoveryVC.swift | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 977ee95e..ae7c596c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -365,8 +365,37 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } } -// MARK: - CourseListCVCDeleagte +// MARK: - UIScrollViewDelegate +extension CourseDiscoveryVC: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + performPagination() + } + + private func performPagination() { + let contentOffsetY = mapCollectionView.contentOffset.y // 우리가 보는 화면 + let collectionViewHeight = mapCollectionView.contentSize.height // 전체 사이즈 + let paginationY = mapCollectionView.bounds.size.height // 유저 화면의 가장 아래 y축 이라고 생각 + + if contentOffsetY > collectionViewHeight - paginationY { + if courseList.count < pageNo * serverResponseNumber { + // 페이지 끝에 도달하면 현재 페이지에 더 이상 데이터가 없음을 의미 + // 새로온 데이터의 갯수가 원래 서버에서 응답에서 온 갯수보다 작으면 페이지네이션 금지 + // 페이지네이션 중단 코드 + return + } + print("🫠\(pageNo)") + if pageNo < totalPageNum { + if !isDataLoaded { + isDataLoaded = true + getCourseData() + pageNo += 1 + isDataLoaded = false + } + } + } + } + extension CourseDiscoveryVC: CourseListCVCDeleagte { func likeButtonTapped(wantsTolike: Bool, index: Int) { guard UserManager.shared.userType != .visitor else { From 7b586387bc7e41bb29264d3a225402d1cc7c1308 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Tue, 5 Dec 2023 21:56:26 +0900 Subject: [PATCH 33/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B2=84=ED=8A=BC=20=EB=8F=99=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 적당한 스크롤 이벤트가 발생하면 업로드 버튼이 다르게 바뀌는 것을 구현하였습니다. --- .../Views/VC/CourseDiscoveryVC.swift | 106 ++++++++++-------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index ae7c596c..bdca1e3c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -22,10 +22,11 @@ final class CourseDiscoveryVC: UIViewController { private var courseList = [PublicCourse]() private var cancelBag = CancelBag() private var specialList = [String]() + private var totalPageNum = 0 private var pageNo = 1 private var sort = "date" private var isDataLoaded = false - private var totalPageNum = 0 + private var uploadButtonChanged = false // MARK: - UIComponents @@ -42,11 +43,12 @@ final class CourseDiscoveryVC: UIViewController { $0.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) } + private let miniUploadButton = UIButton(type: .system).then { + $0.setImage(ImageLiterals.icPlusButton, for: .normal) + } + private let emptyView = ListEmptyView(description: "공유할 수 있는 코스가 없어요!\n코스를 그려주세요", buttonTitle: "코스 그리기") - private let refreshControl = UIRefreshControl() - // refreshControl 사용시 사용 - // 마지막 머지에 사용안하면 삭제 // MARK: - collectionview @@ -114,6 +116,7 @@ extension CourseDiscoveryVC { private func setAddTarget() { self.searchButton.addTarget(self, action: #selector(pushToSearchVC), for: .touchUpInside) self.uploadButton.addTarget(self, action: #selector(pushToDiscoveryVC), for: .touchUpInside) + self.miniUploadButton.addTarget(self, action: #selector(pushToDiscoveryVC), for: .touchUpInside) } // private func setDataLoadIfNeeded() { /// 데이터를 받고 다른 뷰를 갔다가 와도 데이터가 유지되게끔 하기 위한 함수 입니다. (한번만 호출되면 되는 함수!) @@ -129,7 +132,7 @@ extension CourseDiscoveryVC { private func setCombineEvent() { CourseSelectionPublisher.shared.didSelectCourse .sink { [weak self] indexPath in - self?.handleCourseSelection(at: indexPath) + self?.setMarathonCourseSelection(at: indexPath) } .store(in: cancelBag) } @@ -161,6 +164,8 @@ extension CourseDiscoveryVC { view.backgroundColor = .w1 mapCollectionView.backgroundColor = .w1 self.emptyView.isHidden = true + self.miniUploadButton.isHidden = true + self.uploadButton.isHidden = false } private func setNavigationBar() { @@ -178,8 +183,9 @@ extension CourseDiscoveryVC { } private func setLayout() { - view.addSubviews(uploadButton, mapCollectionView) + view.addSubviews(mapCollectionView, uploadButton, miniUploadButton) view.bringSubviewToFront(uploadButton) + view.bringSubviewToFront(miniUploadButton) mapCollectionView.addSubview(emptyView) mapCollectionView.snp.makeConstraints { @@ -194,22 +200,20 @@ extension CourseDiscoveryVC { make.width.equalTo(92) } + miniUploadButton.snp.makeConstraints { make in + make.trailing.equalTo(self.view.safeAreaLayoutGuide).inset(38) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) + make.width.height.equalTo(41) + } + emptyView.snp.makeConstraints { make in make.top.equalTo(naviBar.snp.bottom).offset(300) make.centerX.equalTo(naviBar) } - let shadowView = ShadowView() - self.view.addSubview(shadowView) - - shadowView.snp.makeConstraints { make in - make.trailing.equalTo(self.view.safeAreaLayoutGuide).inset(16) - make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) - make.height.equalTo(40) - make.width.equalTo(92) - } - self.view.bringSubviewToFront(uploadButton) + self.view.bringSubviewToFront(miniUploadButton) + } } @@ -271,30 +275,6 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc } } - func scrollViewDidScroll(_ scrollView: UIScrollView) { - let contentOffsetY = mapCollectionView.contentOffset.y // 우리가 보는 화면 - let collectionViewHeight = mapCollectionView.contentSize.height // 전체 사이즈 - let paginationY = mapCollectionView.bounds.size.height // 유저 화면의 가장 아래 y축 이라고 생각 - - if contentOffsetY > collectionViewHeight - paginationY { - if courseList.count < pageNo * serverResponseNumber { - // 페이지 끝에 도달하면 현재 페이지에 더 이상 데이터가 없음을 의미 - // 새로온 데이터의 갯수가 원래 서버에서 응답에서 온 갯수보다 작으면 페이지네이션 금지 - // 페이지네이션 중단 코드 - return - } - print("🫠\(pageNo)") - if pageNo < totalPageNum { - if !isDataLoaded { - isDataLoaded = true - getCourseData() - pageNo += 1 - isDataLoaded = false - } - } - } - } - private func courseListCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .all) @@ -304,7 +284,6 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scrap, indexPath: indexPath.item) return cell } - } // MARK: - UICollectionViewDelegateFlowLayout @@ -354,9 +333,8 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { } } - private func handleCourseSelection(at indexPath: IndexPath) { - // 여기서 외부에서 Marathon Cell에서 받아오는 indexPath를 처리 합니다. - // 머지전 주석 삭제 + // 외부에서 Marathon Cell에서 받아오는 indexPath를 처리 합니다. + private func setMarathonCourseSelection(at indexPath: IndexPath) { let courseDetailVC = CourseDetailVC() let courseModel = courseList[indexPath.item] courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) @@ -370,6 +348,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { extension CourseDiscoveryVC: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { performPagination() + changeButtonStyleOnScroll() } private func performPagination() { @@ -396,6 +375,40 @@ extension CourseDiscoveryVC: UIScrollViewDelegate { } } + private func changeButtonStyleOnScroll() { + let contentOffsetY = mapCollectionView.contentOffset.y + let scrollThreshold = mapCollectionView.bounds.size.height * 0.3 // 30% 스크롤 했으면 UI 변경 + + if contentOffsetY > scrollThreshold { + handleButtonVisibility(uploadButtonChanged: true, hidden: true, miniHidden: false) + } else { + handleButtonVisibility(uploadButtonChanged: false, hidden: false, miniHidden: true) + } + } + + private func handleButtonVisibility(uploadButtonChanged: Bool, hidden: Bool, miniHidden: Bool) { + guard self.uploadButtonChanged != uploadButtonChanged else { + return + } + + toggleUploadButtons(hidden: hidden, miniHidden: miniHidden) + self.uploadButtonChanged = uploadButtonChanged + } + + private func toggleUploadButtons(hidden: Bool, miniHidden: Bool) { + animateButtonTransition(button: uploadButton, hidden: hidden) + animateButtonTransition(button: miniUploadButton, hidden: miniHidden) + } + + private func animateButtonTransition(button: UIButton, hidden: Bool) { + UIView.transition(with: button, duration: 0.5, options: .transitionFlipFromRight, animations: { + button.isHidden = hidden + }, completion: nil) + } +} + +// MARK: - CourseListCVCDelegate + extension CourseDiscoveryVC: CourseListCVCDeleagte { func likeButtonTapped(wantsTolike: Bool, index: Int) { guard UserManager.shared.userType != .visitor else { @@ -404,7 +417,7 @@ extension CourseDiscoveryVC: CourseListCVCDeleagte { } let publicCourseId = courseList[index].id - scrapCourse(publicCourseId: publicCourseId, scrapTF: wantsTolike) + self.scrapCourse(publicCourseId: publicCourseId, scrapTF: wantsTolike) } } @@ -420,7 +433,6 @@ extension CourseDiscoveryVC { switch response { case .success(let result): let status = result.statusCode - if 200..<300 ~= status { do { let responseDto = try result.map(BaseResponse.self) @@ -433,12 +445,10 @@ extension CourseDiscoveryVC { print(error.localizedDescription) } } - if status >= 400 { print("400 error") self.showNetworkFailureToast() } - case .failure(let error): print(error.localizedDescription) self.showNetworkFailureToast() From 9d862f80b05e7af63bd455ed3c326dae30263dca Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 7 Dec 2023 14:54:45 +0900 Subject: [PATCH 34/40] =?UTF-8?q?[Docs]=20#194=20-=20gitignore=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 11 ++++-- .../Runnect-iOS.xcodeproj/project.pbxproj | 8 ++--- .../GoogleSupport/GoogleService-Info.plist | 34 ------------------- 3 files changed, 13 insertions(+), 40 deletions(-) delete mode 100644 Runnect-iOS/Runnect-iOS/GoogleSupport/GoogleService-Info.plist diff --git a/.gitignore b/.gitignore index 5bafdee7..748d4b1c 100644 --- a/.gitignore +++ b/.gitignore @@ -113,5 +113,12 @@ Gemfile.lock report.xml Runnect-iOS/fastlane/.env.default -## dynamicLink -Runnect-iOS/Runnect-iOS/GoogleService-Info.plist \ No newline at end of file +### Runniest project - git ignore setting ### +Runnect-iOS/Runnect-iOS/Info.plist +Runnect-iOS/Info.plist +Info.plist + +### dynamicLink ### +Runnect-iOS/Runnect-iOS/GoogleService-Info.plist +Runnect-iOS/GoogleService-Info.plist +GoogleService-Info.plist \ No newline at end of file diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 4831ff92..32a3eacc 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ 23EE06D12AC2F44E00CB3FF8 /* TmapAddressSearchingResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EE06D02AC2F44E00CB3FF8 /* TmapAddressSearchingResponseDto.swift */; }; 712F661D2A7B7BAB00D9539B /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712F661C2A7B7BAB00D9539B /* Config.swift */; }; 7136BF8A2AF921A900679364 /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */; }; - 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */; }; + 713BA40B2B218AF8009091A8 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 713BA40A2B218AF8009091A8 /* GoogleService-Info.plist */; }; 717916DA2B13613B009CEF97 /* MarathonListResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */; }; 717916DE2B137DC3009CEF97 /* TotalPageCountDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */; }; 71F7804E2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */; }; @@ -182,9 +182,9 @@ 7110A6032AA337DD009A7E99 /* Runnect-iOSDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Runnect-iOSDebug.entitlements"; sourceTree = ""; }; 712F661C2A7B7BAB00D9539B /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 7136BF892AF921A900679364 /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; + 713BA40A2B218AF8009091A8 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 717916D92B13613B009CEF97 /* MarathonListResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonListResponseDto.swift; sourceTree = ""; }; 717916DD2B137DC3009CEF97 /* TotalPageCountDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalPageCountDto.swift; sourceTree = ""; }; - 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Runnect-iOS/Runnect-iOS/Runnect-iOS/GoogleService-Info.plist"; sourceTree = ""; }; 71F7804D2B0893B600B53253 /* MarathonTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonTitleCollectionViewCell.swift; sourceTree = ""; }; 71F7804F2B0893D700B53253 /* MarathonMapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonMapCollectionViewCell.swift; sourceTree = ""; }; 71F7BF062B0CDFE300B752B3 /* MarathonCourseListCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarathonCourseListCVC.swift; sourceTree = ""; }; @@ -834,7 +834,7 @@ CE6655A9295D7FAA00C64E12 /* Network */, CE6655A8295D7F7D00C64E12 /* Presentation */, CE4545D6295D7AF5003201E1 /* Info.plist */, - 71DBF23D2ABB255A0013415B /* GoogleService-Info.plist */, + 713BA40A2B218AF8009091A8 /* GoogleService-Info.plist */, ); path = "Runnect-iOS"; sourceTree = ""; @@ -1289,7 +1289,7 @@ files = ( CE665615295D989A00C64E12 /* .swiftlint.yml in Resources */, CE17F0342961BEF800E1DED0 /* Pretendard-Bold.otf in Resources */, - 71717B072B063E14004EA8DA /* GoogleService-Info.plist in Resources */, + 713BA40B2B218AF8009091A8 /* GoogleService-Info.plist in Resources */, CE17F0352961BEF800E1DED0 /* Pretendard-SemiBold.otf in Resources */, CE17F0332961BEF800E1DED0 /* Pretendard-Medium.otf in Resources */, CE6655BF295D82E200C64E12 /* .gitkeep in Resources */, diff --git a/Runnect-iOS/Runnect-iOS/GoogleSupport/GoogleService-Info.plist b/Runnect-iOS/Runnect-iOS/GoogleSupport/GoogleService-Info.plist deleted file mode 100644 index 2bf115de..00000000 --- a/Runnect-iOS/Runnect-iOS/GoogleSupport/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - 772504823881-bp4sinq6869e5jaqv57r3pk2l5mmnefe.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.772504823881-bp4sinq6869e5jaqv57r3pk2l5mmnefe - API_KEY - AIzaSyAWjFUbR5CKoP8_V5a56aXON—0DkM1faw - GCM_SENDER_ID - 772504823881 - PLIST_VERSION - 1 - BUNDLE_ID - com.runnect.Runnect-iOS - PROJECT_ID - runnect-6c5e8 - STORAGE_BUCKET - runnect-6c5e8.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:772504823881:ios:e955e328c32c8a864b3798 - - From f90f5d0e58df40f021d4a5a37237a604110c1743 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Thu, 7 Dec 2023 14:55:18 +0900 Subject: [PATCH 35/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B2=84=ED=8A=BC=20=EB=AA=A8=EC=85=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/VC/CourseDiscoveryVC.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index bdca1e3c..a15d1ee5 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -387,9 +387,7 @@ extension CourseDiscoveryVC: UIScrollViewDelegate { } private func handleButtonVisibility(uploadButtonChanged: Bool, hidden: Bool, miniHidden: Bool) { - guard self.uploadButtonChanged != uploadButtonChanged else { - return - } + guard self.uploadButtonChanged != uploadButtonChanged else { return } toggleUploadButtons(hidden: hidden, miniHidden: miniHidden) self.uploadButtonChanged = uploadButtonChanged @@ -401,10 +399,21 @@ extension CourseDiscoveryVC: UIScrollViewDelegate { } private func animateButtonTransition(button: UIButton, hidden: Bool) { - UIView.transition(with: button, duration: 0.5, options: .transitionFlipFromRight, animations: { + let scale: CGFloat = hidden ? 0.1 : 1.0 + let alpha: CGFloat = hidden ? 0.0 : 1.0 + + UIView.animate(withDuration: 0.5, animations: { + button.transform = CGAffineTransform(scaleX: scale, y: scale) + button.alpha = alpha + }) { _ in button.isHidden = hidden - }, completion: nil) + + // Reset transform after animation completes + button.transform = CGAffineTransform.identity + } } + + } // MARK: - CourseListCVCDelegate From 31bd47faf19e2f5c0d67ffdbdd8a0f5f9d1e87d0 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Fri, 8 Dec 2023 20:17:05 +0900 Subject: [PATCH 36/40] =?UTF-8?q?[Feat]=20#194=20-=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B2=84=ED=8A=BC=20=EC=95=A0=EB=8B=88=EB=A9=94?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDiscovery/Views/VC/CourseDiscoveryVC.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index a15d1ee5..a2f72d5f 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -377,7 +377,7 @@ extension CourseDiscoveryVC: UIScrollViewDelegate { private func changeButtonStyleOnScroll() { let contentOffsetY = mapCollectionView.contentOffset.y - let scrollThreshold = mapCollectionView.bounds.size.height * 0.3 // 30% 스크롤 했으면 UI 변경 + let scrollThreshold = mapCollectionView.bounds.size.height * 0.1 // 10% 스크롤 했으면 UI 변경 if contentOffsetY > scrollThreshold { handleButtonVisibility(uploadButtonChanged: true, hidden: true, miniHidden: false) @@ -394,15 +394,15 @@ extension CourseDiscoveryVC: UIScrollViewDelegate { } private func toggleUploadButtons(hidden: Bool, miniHidden: Bool) { - animateButtonTransition(button: uploadButton, hidden: hidden) - animateButtonTransition(button: miniUploadButton, hidden: miniHidden) + animateButtonTransition(button: uploadButton, hidden: hidden, delay: 0) + animateButtonTransition(button: miniUploadButton, hidden: miniHidden, delay: 0.1) // 예시로 0.25초 딜레이 적용 } - private func animateButtonTransition(button: UIButton, hidden: Bool) { + private func animateButtonTransition(button: UIButton, hidden: Bool, delay: TimeInterval) { let scale: CGFloat = hidden ? 0.1 : 1.0 let alpha: CGFloat = hidden ? 0.0 : 1.0 - UIView.animate(withDuration: 0.5, animations: { + UIView.animate(withDuration: 0.5, delay: delay, options: .transitionCurlUp, animations: { button.transform = CGAffineTransform(scaleX: scale, y: scale) button.alpha = alpha }) { _ in @@ -412,8 +412,6 @@ extension CourseDiscoveryVC: UIScrollViewDelegate { button.transform = CGAffineTransform.identity } } - - } // MARK: - CourseListCVCDelegate From 2d7b98ab1a1fefa644cd4322ea32c8ac9a726fb1 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Fri, 8 Dec 2023 22:11:29 +0900 Subject: [PATCH 37/40] =?UTF-8?q?[Add]=20#194=20-=20=EC=BD=94=EC=8A=A4=20?= =?UTF-8?q?=EB=B0=9C=EA=B2=AC=20=ED=8E=98=EC=9D=B4=EC=A7=95=20DTO=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서버에서 만들어준 isEnd, totalPageNum을 추가 해주었습니다. --- .../ResponseDto/PickedMapListResponseDto.swift | 4 +++- .../CourseDiscovery/Views/VC/CourseDiscoveryVC.swift | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/PickedMapListResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/PickedMapListResponseDto.swift index 37097778..b1d3ec2d 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/PickedMapListResponseDto.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/ResponseDto/PickedMapListResponseDto.swift @@ -10,6 +10,8 @@ import Foundation // MARK: - PickedMapListResponseDto struct PickedMapListResponseDto: Codable { + let totalPageSize: Int + let isEnd: Bool let publicCourses: [PublicCourse] } @@ -19,7 +21,7 @@ struct PublicCourse: Codable { let id, courseId: Int let title: String let image: String - let scrap: Bool? + var scrap: Bool? let description: String? let distance: Float? let departure: CourseDiscoveryDeparture diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index a2f72d5f..aa9be369 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -23,6 +23,7 @@ final class CourseDiscoveryVC: UIViewController { private var cancelBag = CancelBag() private var specialList = [String]() private var totalPageNum = 0 + private var isEnd = false private var pageNo = 1 private var sort = "date" private var isDataLoaded = false @@ -444,10 +445,11 @@ extension CourseDiscoveryVC { do { let responseDto = try result.map(BaseResponse.self) guard let data = responseDto.data else { return } - + self.totalPageNum = data.totalPageSize + self.isEnd = data.isEnd self.courseList.append(contentsOf: data.publicCourses) self.mapCollectionView.reloadData() - + print("isEnd= \(self.isEnd), totalPageNum= \(self.totalPageNum)") } catch { print(error.localizedDescription) } From 8e4a2340f747166aca901088378b980381cd5737 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sat, 9 Dec 2023 19:26:53 +0900 Subject: [PATCH 38/40] =?UTF-8?q?Feat=20#194=20-=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=9E=A9=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDetail/VC/CourseDetailVC.swift | 6 ++++ .../Views/TitleCollectionViewCell.swift | 31 ++++++++++++++----- .../Views/VC/CourseDiscoveryVC.swift | 29 +++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift index ed4f7e97..99100549 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -39,6 +39,8 @@ final class CourseDetailVC: UIViewController { private var safariViewController: SFSafariViewController? + weak var delegate: ScrapStateDelegate? + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton) @@ -144,7 +146,11 @@ extension CourseDetailVC { return } + guard let publicCourseId = publicCourseId else { return } + scrapCourse(scrapTF: !sender.isSelected) + delegate?.didUpdateScrapState(publicCourseId: publicCourseId, isScrapped: !sender.isSelected) + print("CourseDetailVC 스크랩 탭🔥publicCourseId=\(publicCourseId), isScrapped은 \(!sender.isSelected)요렇게 변경 ") } @objc func shareButtonTapped() { diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index 57087151..a52bb527 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -41,8 +41,12 @@ class TitleCollectionViewCell: UICollectionViewCell { $0.textColor = UIColor.g2 } - private lazy var dateSortButton = createSortButton(title: "최신순", ordering: "date") - private lazy var scrapSortButton = createSortButton(title: "스크랩순", ordering: "scrap") + private lazy var dateSortButton: UIButton = createSortButton(title: "최신순", ordering: "date").then { + $0.isSelected = true + $0.titleLabel?.font = .h5 + } + + private lazy var scrapSortButton: UIButton = createSortButton(title: "스크랩순", ordering: "scrap") // MARK: - Life cycle @@ -59,24 +63,37 @@ class TitleCollectionViewCell: UICollectionViewCell { // MARK: - Method extension TitleCollectionViewCell { - private func createSortButton(title: String, ordering: String) -> UIButton { let button = UIButton(type: .custom).then { $0.setTitle(title, for: .normal) $0.titleLabel?.font = .b3 - $0.setTitleColor(.m1, for: .normal) - $0.setTitleColor(.g2, for: .disabled) + $0.setTitleColor(.g2, for: .normal) } + + button.setTitleColor(.m1, for: .selected) + button.tapPublisher .sink { [weak self] in guard let self = self else { return } - self.delegate?.didTapSortButton(ordering: ordering) + + self.dateSortButton.isSelected = (button == self.dateSortButton) + self.scrapSortButton.isSelected = (button == self.scrapSortButton) + + self.dateSortButton.setTitleColor(self.dateSortButton.isSelected ? .m1 : .g2, for: .normal) + self.scrapSortButton.setTitleColor(self.scrapSortButton.isSelected ? .m1 : .g2, for: .normal) + + self.dateSortButton.titleLabel?.font = self.dateSortButton.isSelected ? .h5 : .b3 + self.scrapSortButton.titleLabel?.font = self.scrapSortButton.isSelected ? .h5 : .b3 + + if button.isSelected { + self.delegate?.didTapSortButton(ordering: ordering) + } } .store(in: &cancellables) + return button } } - // MARK: - Layout extension TitleCollectionViewCell { diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index aa9be369..1599974d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -11,6 +11,11 @@ import SnapKit import Combine import Moya +protocol ScrapStateDelegate: AnyObject { + func didUpdateScrapState(publicCourseId: Int, isScrapped: Bool) + // 코스 상세 에서 스크랩 누르면 코스발견에 해당 부분 스크랩 누르는 이벤트 전달 +} + final class CourseDiscoveryVC: UIViewController { // MARK: - Properties @@ -32,6 +37,7 @@ final class CourseDiscoveryVC: UIViewController { // MARK: - UIComponents private lazy var naviBar = CustomNavigationBar(self, type: .title).setTitle("코스 발견") + private let searchButton = UIButton(type: .system).then { $0.setImage(ImageLiterals.icSearch, for: .normal) $0.tintColor = .g1 @@ -137,6 +143,14 @@ extension CourseDiscoveryVC { } .store(in: cancelBag) } + + private func reloadCellForCourse(publicCourseId: Int) { + if let index = courseList.firstIndex(where: { $0.id == publicCourseId }) { + let indexPath = IndexPath(item: index, section: Section.courseList) + mapCollectionView.reloadItems(at: [indexPath]) + print("\(indexPath) 교체 되었음") + } + } } // MARK: - @objc Function @@ -327,6 +341,7 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == Section.courseList { let courseDetailVC = CourseDetailVC() + courseDetailVC.delegate = self let courseModel = courseList[indexPath.item] courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) courseDetailVC.hidesBottomBarWhenPushed = true @@ -429,6 +444,20 @@ extension CourseDiscoveryVC: CourseListCVCDeleagte { } } +// MARK: - CourseDetailVCDelegate + +extension CourseDiscoveryVC: ScrapStateDelegate { + func didUpdateScrapState(publicCourseId: Int, isScrapped: Bool) { + // CourseDetail에서 id와 scrap정보를 받아와 여기서 처리 + print("😭CourseDiscoveryVC 에 들어오긴함?") + if let index = courseList.firstIndex(where: { $0.id == publicCourseId }) { + courseList[index].scrap = isScrapped + reloadCellForCourse(publicCourseId: publicCourseId) + print("‼️CourseDiscoveryVC 델리게이트 받음 index=\(index)") + } + } +} + // MARK: - Network extension CourseDiscoveryVC { From 079fff8ae4ee6e87825983ab0c47fd6d5d433087 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sat, 9 Dec 2023 21:57:57 +0900 Subject: [PATCH 39/40] =?UTF-8?q?[Chore]=20#194=20-=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=97=86=EB=8A=94=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDetail/VC/CourseDetailVC.swift | 29 +++++++++---------- .../Views/VC/CourseDiscoveryVC.swift | 19 ++---------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift index 99100549..28548e56 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -23,6 +23,8 @@ final class CourseDetailVC: UIViewController { // MARK: - Properties + weak var delegate: ScrapStateDelegate? + private let scrapProvider = Providers.scrapProvider private let PublicCourseProvider = Providers.publicCourseProvider @@ -37,10 +39,6 @@ final class CourseDetailVC: UIViewController { private var publicCourseId: Int? private var isMyCourse: Bool? - private var safariViewController: SFSafariViewController? - - weak var delegate: ScrapStateDelegate? - // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton) @@ -157,36 +155,36 @@ extension CourseDetailVC { guard let model = self.uploadedCourseDetailModel else { return } - + let publicCourse = model.publicCourse let title = publicCourse.title let courseId = publicCourse.id // primaryKey let description = publicCourse.description let courseImage = publicCourse.image - + let dynamicLinksDomainURIPrefix = "https://runnect.page.link" guard let link = URL(string: "\(dynamicLinksDomainURIPrefix)/?courseId=\(courseId)") else { return } - + guard let linkBuilder = DynamicLinkComponents(link: link, domainURIPrefix: dynamicLinksDomainURIPrefix) else { return } - + linkBuilder.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.runnect.Runnect-iOS") linkBuilder.iOSParameters?.appStoreID = "1663884202" linkBuilder.iOSParameters?.minimumAppVersion = "1.0.4" - + linkBuilder.socialMetaTagParameters = DynamicLinkSocialMetaTagParameters() linkBuilder.socialMetaTagParameters?.imageURL = URL(string: courseImage) linkBuilder.socialMetaTagParameters?.title = title linkBuilder.socialMetaTagParameters?.descriptionText = description - + guard let longDynamicLink = linkBuilder.url else { return } print("The long URL is: \(longDynamicLink)") - + /// 짧은 Dynamic Link로 변환하는 부분 입니다. linkBuilder.shorten { [weak self] url, warnings, error in guard let shortDynamicLink = url else { @@ -195,9 +193,9 @@ extension CourseDetailVC { } return } - + print("🔥The short URL is: \(shortDynamicLink)") - + DispatchQueue.main.async { let activityVC = UIActivityViewController(activityItems: [shortDynamicLink.absoluteString], applicationActivities: nil) activityVC.popoverPresentationController?.sourceView = self?.view @@ -205,7 +203,7 @@ extension CourseDetailVC { } } } - + @objc func startButtonDidTap() { guard handleVisitor() else { return } guard let courseId = self.courseId else { return } @@ -229,7 +227,7 @@ extension CourseDetailVC { $0.dataSource = items $0.textFont = .b3 } - + menu.customCellConfiguration = { (index: Index, _: String, cell: DropDownCell) -> Void in let lastDividerLineRemove = UIView(frame: CGRect(origin: CGPoint(x: 0, y: isMyCourse ? 79 : 39), size: CGSize(width: 170, height: 10))) lastDividerLineRemove.backgroundColor = .white @@ -314,7 +312,6 @@ extension CourseDetailVC { // MARK: - Layout Helpers extension CourseDetailVC { - private func setNavigationBar() { view.addSubview(navibar) view.addSubview(moreButton) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 1599974d..2b715adc 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -80,15 +80,12 @@ final class CourseDiscoveryVC: UIViewController { setLayout() setAddTarget() setCombineEvent() - // getTotalPageNum() self.getCourseData() - self.totalPageNum = 5 // test 코드 } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.hideTabBar(wantsToHide: false) - // 여기에 기존 데이터 한번에 불러오는 코스 작성 page 순서만 asyc로 작업 } } @@ -109,7 +106,6 @@ extension CourseDiscoveryVC { } private func register() { - let cellTypes: [UICollectionViewCell.Type] = [AdImageCollectionViewCell.self, MarathonTitleCollectionViewCell.self, MarathonMapCollectionViewCell.self, @@ -126,16 +122,6 @@ extension CourseDiscoveryVC { self.miniUploadButton.addTarget(self, action: #selector(pushToDiscoveryVC), for: .touchUpInside) } - // private func setDataLoadIfNeeded() { /// 데이터를 받고 다른 뷰를 갔다가 와도 데이터가 유지되게끔 하기 위한 함수 입니다. (한번만 호출되면 되는 함수!) - // if !isDataLoaded { - // courseList.removeAll() - // pageNo = 1 - // mapCollectionView.reloadData() - // getCourseData() - // isDataLoaded = true - // } - // } - private func setCombineEvent() { CourseSelectionPublisher.shared.didSelectCourse .sink { [weak self] indexPath in @@ -148,7 +134,7 @@ extension CourseDiscoveryVC { if let index = courseList.firstIndex(where: { $0.id == publicCourseId }) { let indexPath = IndexPath(item: index, section: Section.courseList) mapCollectionView.reloadItems(at: [indexPath]) - print("\(indexPath) 교체 되었음") + print("\(indexPath) 부분 스크랩 교체 되었음") } } } @@ -449,7 +435,6 @@ extension CourseDiscoveryVC: CourseListCVCDeleagte { extension CourseDiscoveryVC: ScrapStateDelegate { func didUpdateScrapState(publicCourseId: Int, isScrapped: Bool) { // CourseDetail에서 id와 scrap정보를 받아와 여기서 처리 - print("😭CourseDiscoveryVC 에 들어오긴함?") if let index = courseList.firstIndex(where: { $0.id == publicCourseId }) { courseList[index].scrap = isScrapped reloadCellForCourse(publicCourseId: publicCourseId) @@ -466,7 +451,7 @@ extension CourseDiscoveryVC { DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { [self] in publicCourseProvider.request(.getCourseData(pageNo: pageNo, sort: sort)) { response in LoadingIndicator.hideLoading() - print("‼️ 이번 sort 는 요?? \(self.sort) ‼️\n") + print("‼️ sort= \(self.sort) ‼️\n") switch response { case .success(let result): let status = result.statusCode From c30d8d2ab944ccd8731ed92adf001b65a38c5249 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sun, 10 Dec 2023 00:21:33 +0900 Subject: [PATCH 40/40] =?UTF-8?q?[Fix]=20#194=20-=20getCourseData=20?= =?UTF-8?q?=EB=A1=9C=EB=94=A9=20=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourseDiscovery/Views/VC/CourseDiscoveryVC.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 2b715adc..ac7f271d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -447,8 +447,8 @@ extension CourseDiscoveryVC: ScrapStateDelegate { extension CourseDiscoveryVC { private func getCourseData() { - LoadingIndicator.showLoading() // 항상 0.7초 늦게 로딩이 되어 버림 0.7초를 넣은 이유는 pagination을 구현할때 한번에 다 받아오지 않게 하기 위함 - DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { [self] in + LoadingIndicator.showLoading() // 항상 0.5초 늦게 로딩이 되어 버림 0.5초를 넣은 이유는 pagination을 구현할때 한번에 다 받아오지 않게 하기 위함 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in publicCourseProvider.request(.getCourseData(pageNo: pageNo, sort: sort)) { response in LoadingIndicator.hideLoading() print("‼️ sort= \(self.sort) ‼️\n")