diff --git a/GEON-PPANG-iOS.xcodeproj/project.pbxproj b/GEON-PPANG-iOS.xcodeproj/project.pbxproj index cb36662a..986a2272 100644 --- a/GEON-PPANG-iOS.xcodeproj/project.pbxproj +++ b/GEON-PPANG-iOS.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 098716B62A60F52200538D05 /* BakeryTypeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098716B52A60F52200538D05 /* BakeryTypeProtocol.swift */; }; 098716B82A6138BD00538D05 /* MyReviewsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098716B72A6138BD00538D05 /* MyReviewsHeaderView.swift */; }; 0987288D2A5BA1F000A29402 /* BookmarkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0987288C2A5BA1F000A29402 /* BookmarkButton.swift */; }; + 098800A82C4E3A62009E6F43 /* UIViewController+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098800A72C4E3A62009E6F43 /* UIViewController+Combine.swift */; }; 098F32EA2A4200FE0092D09A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 098F32E92A4200FE0092D09A /* Assets.xcassets */; }; 098F32ED2A4200FE0092D09A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 098F32EB2A4200FE0092D09A /* LaunchScreen.storyboard */; }; 09980CFB2A99A9800098550C /* SignInPropertyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09980CFA2A99A9800098550C /* SignInPropertyType.swift */; }; @@ -399,6 +400,7 @@ 098716B52A60F52200538D05 /* BakeryTypeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryTypeProtocol.swift; sourceTree = ""; }; 098716B72A6138BD00538D05 /* MyReviewsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyReviewsHeaderView.swift; sourceTree = ""; }; 0987288C2A5BA1F000A29402 /* BookmarkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkButton.swift; sourceTree = ""; }; + 098800A72C4E3A62009E6F43 /* UIViewController+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Combine.swift"; sourceTree = ""; }; 098F32DD2A4200FD0092D09A /* GEON-PPANG-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GEON-PPANG-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 098F32E02A4200FD0092D09A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 098F32E22A4200FD0092D09A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -1028,6 +1030,22 @@ path = View; sourceTree = ""; }; + 098800A52C4E3A3B009E6F43 /* Extension */ = { + isa = PBXGroup; + children = ( + 098800A62C4E3A4D009E6F43 /* Combine */, + ); + path = Extension; + sourceTree = ""; + }; + 098800A62C4E3A4D009E6F43 /* Combine */ = { + isa = PBXGroup; + children = ( + 098800A72C4E3A62009E6F43 /* UIViewController+Combine.swift */, + ); + path = Combine; + sourceTree = ""; + }; 098F32D42A4200FD0092D09A = { isa = PBXGroup; children = ( @@ -1498,6 +1516,7 @@ 3E8C47E02BC3B81B00919E06 /* Utils */ = { isa = PBXGroup; children = ( + 098800A52C4E3A3B009E6F43 /* Extension */, 3E8C47E12BC3B82500919E06 /* DummyUIUtils.swift */, ); path = Utils; @@ -2343,6 +2362,7 @@ 3E3DE9C92C25AE0100CA5999 /* PasswordValidationError.swift in Sources */, 0959F1702A658CA400E77CAF /* HomeBestBakeryResponseDTO.swift in Sources */, 3E7B21C42A621A9A00C8F8B4 /* SortBakeryCollectionViewCell.swift in Sources */, + 098800A82C4E3A62009E6F43 /* UIViewController+Combine.swift in Sources */, DFB587BB2A5D588500704B6C /* DrawDashLineView.swift in Sources */, 090556222A51DB3300752067 /* UIFont+.swift in Sources */, 092698892C3CF93F00A9349D /* BestBakeryResponseDTO.swift in Sources */, diff --git a/GEON-PPANG-iOS/Presentation/Scene/Home/Home/ViewController/NewHomeViewController.swift b/GEON-PPANG-iOS/Presentation/Scene/Home/Home/ViewController/NewHomeViewController.swift index 8af5eab5..b74b92d1 100644 --- a/GEON-PPANG-iOS/Presentation/Scene/Home/Home/ViewController/NewHomeViewController.swift +++ b/GEON-PPANG-iOS/Presentation/Scene/Home/Home/ViewController/NewHomeViewController.swift @@ -18,7 +18,7 @@ final class NewHomeViewController: UIViewController { private let viewModel: any ViewModelType private var cancelBag: Set = Set() - private let viewWillAppearPublisher: PassthroughSubject = PassthroughSubject() + private let cellTappedPublisher: PassthroughSubject = PassthroughSubject() private var bakeryList: [BestBakery] = [] private var reviewList: [BestReview] = [] @@ -34,11 +34,15 @@ final class NewHomeViewController: UIViewController { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: self.layout()) collectionView.showsVerticalScrollIndicator = false collectionView.backgroundColor = .gbbBackground1 - collectionView.register(HomeBakeryCollectionViewCell.self, forCellWithReuseIdentifier: HomeBakeryCollectionViewCell.identifier) - collectionView.register(HomeReviewCollectionViewCell.self, forCellWithReuseIdentifier: HomeReviewCollectionViewCell.identifier) + collectionView.register(HomeBakeryCollectionViewCell.self, + forCellWithReuseIdentifier: HomeBakeryCollectionViewCell.identifier) + collectionView.register(HomeReviewCollectionViewCell.self, + forCellWithReuseIdentifier: HomeReviewCollectionViewCell.identifier) collectionView.register(HomeBottomCollectionViewCell.self, forCellWithReuseIdentifier: HomeBottomCollectionViewCell.identifier) - collectionView.register(HomeHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HomeHeaderView.identifier) + collectionView.register(HomeHeaderView.self, + forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HomeHeaderView.identifier) collectionView.dataSource = self + collectionView.delegate = self return collectionView }() @@ -56,12 +60,6 @@ final class NewHomeViewController: UIViewController { // MARK: - Life cycle - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - viewWillAppearPublisher.send() - } - override func viewDidLoad() { super.viewDidLoad() @@ -99,9 +97,7 @@ final class NewHomeViewController: UIViewController { private func transformedOutput() -> HomeViewModel.Output? { guard let viewModel = self.viewModel as? HomeViewModel else { return nil } - let input = HomeViewModel.Input( - viewWillAppear: self.viewWillAppearPublisher - ) + let input = HomeViewModel.Input(viewDidLoad: self.viewDidLoadPublisher) return viewModel.transform(input) } @@ -113,7 +109,6 @@ final class NewHomeViewController: UIViewController { .sink { err in print("error:\(err)") } receiveValue: { [weak self] bakery in - dump(bakery) self?.updateBakery(bakery: bakery) } .store(in: &self.cancelBag) @@ -123,7 +118,6 @@ final class NewHomeViewController: UIViewController { .sink { err in print("error:\(err)") } receiveValue: { [weak self] review in - dump(review) self?.updateReview(review: review) } .store(in: &self.cancelBag) @@ -131,15 +125,100 @@ final class NewHomeViewController: UIViewController { private func updateBakery(bakery:[BestBakery]) { self.bakeryList = bakery - self.collectionView.reloadSections(IndexSet(integersIn: 0 ..< 1)) + UIView.performWithoutAnimation { + self.collectionView.reloadSections(IndexSet(integer: 0)) + + } } private func updateReview(review: [BestReview]) { self.reviewList = review - self.collectionView.reloadSections(IndexSet(integersIn: 1 ..< 2)) + UIView.performWithoutAnimation { + self.collectionView.reloadSections(IndexSet(integer: 1)) + } } } +// MARK: - CollectionView Layout + +extension NewHomeViewController { + + private func layout() -> UICollectionViewCompositionalLayout { + return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ in + switch sectionIndex { + case 0: + return self?.bestSection(headerSize: 49) + case 1: + return self?.bestSection(headerSize: 25) + default: + return self?.bottomSection() + } + }) + } + + private func bestSection(headerSize: CGFloat) -> NSCollectionLayoutSection { + + let itemGroupSize = NSCollectionLayoutSize( + widthDimension: .absolute(convertByWidthRatio(192)), + heightDimension: .absolute(heightConsideringNotch(236)) + ) + let item = NSCollectionLayoutItem(layoutSize: itemGroupSize) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemGroupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + section.interGroupSpacing = 12 + section.orthogonalScrollingBehavior = .continuous + section.boundarySupplementaryItems = bestSectionHeader(to: headerSize) + section.contentInsets = NSDirectionalEdgeInsets(top: 24, + leading: 24, + bottom: 30, + trailing: 24) + + return section + } + + private func bottomSection() -> NSCollectionLayoutSection { + + let itemGroupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1), + heightDimension: .absolute(72) + ) + let item = NSCollectionLayoutItem(layoutSize: itemGroupSize) + + let group = NSCollectionLayoutGroup.vertical( + layoutSize: itemGroupSize, + subitem: item, + count: 1 + ) + + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 14, + leading: 24, + bottom: 30, + trailing: 24) + + return section + } + + private func bestSectionHeader(to size: CGFloat) -> [NSCollectionLayoutBoundarySupplementaryItem] { + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1), + heightDimension: .absolute(size) + ) + + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .top + ) + + return [header] + } +} + +// MARK: - CollectionView DataSource + extension NewHomeViewController: UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { @@ -187,66 +266,45 @@ extension NewHomeViewController: UICollectionViewDataSource { guard let title = Sections(rawValue: indexPath.section)?.title else { return UICollectionReusableView() } // nickname - header.configureSectionHeaderTitle(nil, title) + header.configureSectionHeaderTitle("nil", title) return header } } -extension NewHomeViewController { - private func layout() -> UICollectionViewCompositionalLayout { - - return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ in - switch sectionIndex { - case 0: - return self?.bestSection(headerSize: 49) - case 1: - return self?.bestSection(headerSize: 25) - default: - return self?.bottomSection() - } - }) - } +// MARK: - CollectionView Delegate + +extension NewHomeViewController: UICollectionViewDelegate { - private func bestSection(headerSize: CGFloat) -> NSCollectionLayoutSection { - - let itemGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(convertByWidthRatio(192)), - heightDimension: .absolute(heightConsideringNotch(236))) - let item = NSCollectionLayoutItem(layoutSize: itemGroupSize) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemGroupSize, subitems: [item]) - - let section = NSCollectionLayoutSection(group: group) - section.interGroupSpacing = 12 - section.orthogonalScrollingBehavior = .continuous + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), - heightDimension: .absolute(headerSize)) - let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, - elementKind: UICollectionView.elementKindSectionHeader, - alignment: .top) - section.contentInsets = NSDirectionalEdgeInsets(top: 24, - leading: 24, - bottom: 30, - trailing: 24) + let (id, bakery) = getBakeryData(for: indexPath) - section.boundarySupplementaryItems = [header] - return section + if let id = id, let bakery = bakery { + navigateToDetailViewController(with: id) + logAnalytics(for: bakery) + } } - private func bottomSection() -> NSCollectionLayoutSection { - - let itemGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), - heightDimension: .absolute(72)) - let item = NSCollectionLayoutItem(layoutSize: itemGroupSize) - - let group = NSCollectionLayoutGroup.vertical(layoutSize: itemGroupSize, - subitem: item, - count: 1) - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 14, - leading: 24, - bottom: 30, - trailing: 24) - - return section + private func getBakeryData(for indexPath: IndexPath) -> (Int?, String?) { + switch indexPath.section { + case 0: + return (bakeryList[indexPath.item].overview.id, bakeryList[indexPath.item].overview.name) + case 1: + return (reviewList[indexPath.item].overview.id, reviewList[indexPath.item].overview.name) + default: + return (nil, nil) + } + } + + private func navigateToDetailViewController(with id: Int) { + let nextViewController = BakeryDetailViewController() + nextViewController.bakeryID = id + navigationController?.isNavigationBarHidden = true + navigationController?.pushViewController(nextViewController, animated: true) + } + + private func logAnalytics(for bakery: String) { + AnalyticManager.log(event: .home(.clickRecommendStore(bakery: bakery))) + AnalyticManager.log(event: .detail(.viewDetailpageAt(source: AnalyticEventType.HOME.rawValue))) } }