diff --git a/Oliveyoung-iOS/Oliveyoung-iOS.xcodeproj/project.pbxproj b/Oliveyoung-iOS/Oliveyoung-iOS.xcodeproj/project.pbxproj index 2720c47..8523c52 100644 --- a/Oliveyoung-iOS/Oliveyoung-iOS.xcodeproj/project.pbxproj +++ b/Oliveyoung-iOS/Oliveyoung-iOS.xcodeproj/project.pbxproj @@ -9,8 +9,9 @@ /* Begin PBXBuildFile section */ 3709E087292A394300222F3E /* ProductModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3709E086292A394300222F3E /* ProductModel.swift */; }; 37A27091292B7CE8008855D0 /* PretendardVariable.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 37A27090292B7CE8008855D0 /* PretendardVariable.ttf */; }; + 37CB1C46292E2FAA00C94725 /* relateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB1C45292E2FAA00C94725 /* relateCollectionViewCell.swift */; }; 37E0023329262A1A00283615 /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E0023229262A1A00283615 /* TagCollectionViewCell.swift */; }; - 37E0023529262A2800283615 /* ProductCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E0023429262A2800283615 /* ProductCollectionViewCell.swift */; }; + 37E0023529262A2800283615 /* recommendCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E0023429262A2800283615 /* recommendCollectionViewCell.swift */; }; DD0FC2962924C38500ACF342 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0FC2952924C38500ACF342 /* DetailViewController.swift */; }; DD0FC2982924C39800ACF342 /* SearchResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0FC2972924C39800ACF342 /* SearchResultViewController.swift */; }; DD0FC29A2924C39E00ACF342 /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0FC2992924C39E00ACF342 /* SearchViewController.swift */; }; @@ -46,8 +47,9 @@ /* Begin PBXFileReference section */ 3709E086292A394300222F3E /* ProductModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductModel.swift; sourceTree = ""; }; 37A27090292B7CE8008855D0 /* PretendardVariable.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = PretendardVariable.ttf; sourceTree = ""; }; + 37CB1C45292E2FAA00C94725 /* relateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = relateCollectionViewCell.swift; sourceTree = ""; }; 37E0023229262A1A00283615 /* TagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagCollectionViewCell.swift; sourceTree = ""; }; - 37E0023429262A2800283615 /* ProductCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductCollectionViewCell.swift; sourceTree = ""; }; + 37E0023429262A2800283615 /* recommendCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = recommendCollectionViewCell.swift; sourceTree = ""; }; DD0FC2952924C38500ACF342 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; DD0FC2972924C39800ACF342 /* SearchResultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultViewController.swift; sourceTree = ""; }; DD0FC2992924C39E00ACF342 /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = ""; }; @@ -267,10 +269,11 @@ DDC28C8E29232F4C003B87BB /* Detail */ = { isa = PBXGroup; children = ( - DD0FC2952924C38500ACF342 /* DetailViewController.swift */, - 3709E086292A394300222F3E /* ProductModel.swift */, 37E0023229262A1A00283615 /* TagCollectionViewCell.swift */, - 37E0023429262A2800283615 /* ProductCollectionViewCell.swift */, + 37E0023429262A2800283615 /* recommendCollectionViewCell.swift */, + 37CB1C45292E2FAA00C94725 /* relateCollectionViewCell.swift */, + 3709E086292A394300222F3E /* ProductModel.swift */, + DD0FC2952924C38500ACF342 /* DetailViewController.swift */, ); path = Detail; sourceTree = ""; @@ -375,6 +378,7 @@ DD0FC2962924C38500ACF342 /* DetailViewController.swift in Sources */, DDC28C8929232E52003B87BB /* UILabel+.swift in Sources */, DD0FC29C2924C3A500ACF342 /* HomeViewController.swift in Sources */, + 37CB1C46292E2FAA00C94725 /* relateCollectionViewCell.swift in Sources */, DDC28C7D29232CD6003B87BB /* UIImageView+.swift in Sources */, DDC28C8329232D75003B87BB /* Identifier.swift in Sources */, 37E0023329262A1A00283615 /* TagCollectionViewCell.swift in Sources */, @@ -383,7 +387,7 @@ DDC28C8729232E0E003B87BB /* UIView+.swift in Sources */, DDC28C54292320E0003B87BB /* SceneDelegate.swift in Sources */, DD0FC2982924C39800ACF342 /* SearchResultViewController.swift in Sources */, - 37E0023529262A2800283615 /* ProductCollectionViewCell.swift in Sources */, + 37E0023529262A2800283615 /* recommendCollectionViewCell.swift in Sources */, DDC28C7F29232D27003B87BB /* Const.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/DetailViewController.swift b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/DetailViewController.swift index 761644f..59dd6e2 100644 --- a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/DetailViewController.swift +++ b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/DetailViewController.swift @@ -41,6 +41,18 @@ final class DetailViewController: UIViewController { return collectionView }() + private lazy var relateCollectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .clear + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.isScrollEnabled = true + collectionView.showsVerticalScrollIndicator = false + return collectionView + }() + //MARK: - Components private let topContainerView = UIView() private let productImageContainerView = UIView() @@ -184,6 +196,16 @@ final class DetailViewController: UIViewController { $0.textColor = 0x2f2f2f.color } + private let recommendLabel = UILabel().then { + $0.font = .tittleSubhead1 + $0.textColor = 0x2f2f2f.color + } + + private let relateLabel = UILabel().then { + $0.font = .tittleSubhead1 + $0.textColor = 0x2f2f2f.color + } + private let rateImageView = UIImageView(image: UIImage(named: "starRate")) private let singleStarImageView = UIImageView(image: UIImage(named: "star14X14")) private let productImageView = UIImageView(image: UIImage(named: "detailView")) @@ -193,13 +215,16 @@ final class DetailViewController: UIViewController { //MARK: - Variables var tagList = ["립밤", "핸드크림", "틴트", "쿠션", "마스크팩"] - var productList:[productModel] = [ - productModel(name: "아이소이", description:"엔젤 아쿠아 수분 진정 크림 150ml모이스춰닥터 장/수/진 수분 앰플 기획" , productImage: "", price: "27,000", discountRate:""), - productModel(name: "센카", description: "퍼펙트 휩 페이셜 위시 120g", productImage: "", price: "8,500", discountRate: ""), - productModel(name: "라운드랩", description: "1025 독도 앰플 45g", productImage: "", price: "28,000", discountRate: ""), - productModel(name: "피지오겔", description: "[한정기획] AI크림 100ml 기획", productImage: "", price: "27,000", discountRate: "23%"), - productModel(name: "에스트라", description: "아토베리어 365 하이드로 에센스 200ml ", productImage: "", price: "21,600", discountRate: "32%"), - productModel(name: "아벤느", description: "시칼파트플러스 크림 1+1 기획", productImage: "", price: "19,310", discountRate: "5%") + var recommendList:[recommendModel] = [ + recommendModel(name: "아이소이", description:"엔젤 아쿠아 수분 진정 크림 150ml모이스춰닥터 장/수/진 수분 앰플 기획" , productImage: "", price: "27,000"), + recommendModel(name: "센카", description: "퍼펙트 휩 페이셜 위시 120g", productImage: "", price: "8,500"), + recommendModel(name: "라운드랩", description: "1025 독도 앰플 45g", productImage: "", price: "28,000") + ] + + var relateList:[relateModel] = [ + relateModel(name: "피지오겔", description: "[한정기획] AI크림 100ml 기획", productImage: "", price: "27,000", discountRate: "23%"), + relateModel(name: "에스트라", description: "아토베리어 365 하이드로 에센스 200ml ", productImage: "", price: "21,600", discountRate: "32%"), + relateModel(name: "아벤느", description: "시칼파트플러스 크림 1+1 기획", productImage: "", price: "19,310", discountRate: "5%") ] @@ -209,10 +234,15 @@ final class DetailViewController: UIViewController { final let tagInterItemSpacing: CGFloat = 8 final let tagCellHeight: CGFloat = 27 - final let productInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - final let productLineSpacing: CGFloat = 0 - final let productInterItemSpacing: CGFloat = 15 - final let productCellHeight: CGFloat = 148 + final let recommendInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + final let recommendLineSpacing: CGFloat = 0 + final let recommendInterItemSpacing: CGFloat = 15 + final let recommendCellHeight: CGFloat = 148 + + final let relateInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + final let relateLineSpacing: CGFloat = 0 + final let relateInterItemSpacing: CGFloat = 15 + final let relateCellHeight: CGFloat = 148 //MARK: - Life Cycles override func viewDidLoad() { @@ -301,6 +331,16 @@ extension DetailViewController { ) productDetailContainerView.addSubview(productImageView) + recommendContainerView.addSubviews( + recommendLabel, + recommendCollectionView + ) + + relatedProductContainerView.addSubviews( + relateLabel, + relateCollectionView + ) + // ContainerViews topContainerView.snp.makeConstraints { $0.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) @@ -594,6 +634,29 @@ extension DetailViewController { $0.edges.equalToSuperview() } + //recommendContainerView + recommendLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(16) + $0.leading.equalToSuperview() + } + + recommendCollectionView.snp.makeConstraints { + $0.top.equalTo(recommendLabel.snp.bottom).offset(16) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(204) + } + + //relateContainerView + relateLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(16) + $0.leading.equalToSuperview() + } + + relateCollectionView.snp.makeConstraints { + $0.top.equalTo(relateLabel.snp.bottom).offset(16) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(192) + } } //MARK: - General Helpers @@ -615,8 +678,8 @@ extension DetailViewController { // availableStoreContainerView.backgroundColor = .systemYellow // tabbarButtonContainerView.backgroundColor = .systemGreen // productDetailContainerView.backgroundColor = .systemBlue - recommendContainerView.backgroundColor = .systemPurple - relatedProductContainerView.backgroundColor = .systemCyan +// recommendContainerView.backgroundColor = .systemPurple +// relatedProductContainerView.backgroundColor = .systemCyan bottomContainerView.backgroundColor = .systemGray6 } @@ -643,6 +706,8 @@ extension DetailViewController { self.availableStoreLabel.text = "홍대공항철도점" self.storeStatusLabel.text = "영업중" self.stockLabel.text = "재고보유 가능성 높음" + self.relateLabel.text = "이 상품은 어떠세요?" + self.recommendLabel.text = "방금 본 것과 유사한 상품이에요" } private func viewConfig() { @@ -659,13 +724,16 @@ extension DetailViewController { private func configDelegate() { tagCollectionView.delegate = self tagCollectionView.dataSource = self -// productCollectionView.delegate = self -// productCollectionView.dataSource = self + recommendCollectionView.delegate = self + recommendCollectionView.dataSource = self + relateCollectionView.delegate = self + relateCollectionView.dataSource = self } private func register() { tagCollectionView.register(TagCollectionViewCell.self, forCellWithReuseIdentifier: TagCollectionViewCell.identifier) -// productCollectionView.register(ProductCollectionViewCell.self, forCellWithReuseIdentifier: ProductCollectionViewCell.identifier) + recommendCollectionView.register(recommendCollectionViewCell.self, forCellWithReuseIdentifier: recommendCollectionViewCell.identifier) + relateCollectionView.register(relateCollectionViewCell.self, forCellWithReuseIdentifier: relateCollectionViewCell.identifier) } //MARK: - Action Helpers @@ -675,34 +743,77 @@ extension DetailViewController { //MARK: - UICollectionViewDelegateFlowLayout extension DetailViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - - let tempLabel: UILabel = UILabel() - tempLabel.text = tagList[indexPath.item] - return CGSize(width: tempLabel.intrinsicContentSize.width, height: 27) + if collectionView == tagCollectionView { + let tempLabel: UILabel = UILabel() + tempLabel.text = tagList[indexPath.item] + return CGSize(width: tempLabel.intrinsicContentSize.width, height: 27) + } else if collectionView == recommendCollectionView { + return CGSize(width: 105, height: 204) + } else { + return CGSize(width: 105, height: 192) + } } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { - return tagLineSpacing + if collectionView == tagCollectionView { + return tagLineSpacing + } else if collectionView == recommendCollectionView { + return recommendLineSpacing + } else { + return relateLineSpacing + } } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { - return tagInterItemSpacing + if collectionView == tagCollectionView { + return tagInterItemSpacing + } else if collectionView == recommendCollectionView { + return recommendInterItemSpacing + } else { + return relateInterItemSpacing + } } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { - return tagInset + if collectionView == tagCollectionView { + return tagInset + } else if collectionView == recommendCollectionView { + return recommendInset + } else { + return relateInset + } } } //MARK: - UICollectionViewDataSource extension DetailViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return tagList.count + + if collectionView == tagCollectionView { + return tagList.count + } else if collectionView == recommendCollectionView { + return 3 + } else { + return 3 + } + } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let tagCell = collectionView.dequeueReusableCell(withReuseIdentifier: TagCollectionViewCell.identifier, for: indexPath) as? TagCollectionViewCell else { return UICollectionViewCell() } - tagCell.dataBind(tag: tagList[indexPath.item]) - return tagCell + if collectionView == tagCollectionView { + guard let tagCell = collectionView.dequeueReusableCell(withReuseIdentifier: TagCollectionViewCell.identifier, for: indexPath) as? TagCollectionViewCell else { return UICollectionViewCell() } + tagCell.dataBind(tag: tagList[indexPath.item]) + return tagCell + } else if collectionView == recommendCollectionView { + guard let recommendCell = collectionView.dequeueReusableCell(withReuseIdentifier: recommendCollectionViewCell.identifier, for: indexPath) as? + recommendCollectionViewCell else { return UICollectionViewCell() } + recommendCell.dataBind(model: recommendList[indexPath.item]) + return recommendCell + } else { + guard let relateCell = collectionView.dequeueReusableCell(withReuseIdentifier: relateCollectionViewCell.identifier, for: indexPath) as? + relateCollectionViewCell else { return UICollectionViewCell() } + relateCell.dataBind(model: relateList[indexPath.item]) + return relateCell + } } } diff --git a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductModel.swift b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductModel.swift index b1492e7..1c5c975 100644 --- a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductModel.swift +++ b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductModel.swift @@ -7,7 +7,14 @@ import Foundation -struct productModel { +struct recommendModel { + var name: String + var description: String + var productImage: String + var price: String +} + +struct relateModel { var name: String var description: String var productImage: String diff --git a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductCollectionViewCell.swift b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/recommendCollectionViewCell.swift similarity index 91% rename from Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductCollectionViewCell.swift rename to Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/recommendCollectionViewCell.swift index 03e750e..c280197 100644 --- a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/ProductCollectionViewCell.swift +++ b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/recommendCollectionViewCell.swift @@ -12,7 +12,7 @@ import SnapKit import SwiftyColor //MARK: - ProductCollectionViewCell -final class ProductCollectionViewCell: UICollectionViewCell { +final class recommendCollectionViewCell: UICollectionViewCell { //MARK: - UI Components @@ -20,15 +20,18 @@ final class ProductCollectionViewCell: UICollectionViewCell { private let productLabel = UILabel().then { $0.font = .bodyBody2 $0.textColor = 0x2f2f2f.color + $0.numberOfLines = 0 } private let descriptionLabel = UILabel().then { $0.font = .bodyCaption $0.textColor = 0x2f2f2f.color + $0.numberOfLines = 0 } private let priceLabel = UILabel().then { $0.font = .bodyCaption2 $0.textColor = 0x2f2f2f.color + $0.numberOfLines = 0 } private let wonLabel = UILabel().then { $0.font = .bodyCaption2 @@ -51,7 +54,7 @@ final class ProductCollectionViewCell: UICollectionViewCell { } //MARK: - Extensions -extension ProductCollectionViewCell { +extension recommendCollectionViewCell { //MARK: - Layout Helpers private func layout() { @@ -92,7 +95,7 @@ extension ProductCollectionViewCell { //MARK: - General Helpers - func dataBind(model: productModel) { + func dataBind(model: recommendModel) { productImageView.image = UIImage(named: model.productImage) productLabel.text = model.name descriptionLabel.text = model.description diff --git a/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/relateCollectionViewCell.swift b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/relateCollectionViewCell.swift new file mode 100644 index 0000000..198f3db --- /dev/null +++ b/Oliveyoung-iOS/Oliveyoung-iOS/Source/Presentation/Detail/relateCollectionViewCell.swift @@ -0,0 +1,117 @@ +// +// relateCollectionViewCell.swift +// Oliveyoung-iOS +// +// Created by Joon Baek on 2022/11/23. +// + +import UIKit + +import Then +import SnapKit +import SwiftyColor + +//MARK: - ProductCollectionViewCell +final class relateCollectionViewCell: UICollectionViewCell { + + //MARK: - UI Components + + private let productImageView = UIImageView() + private let productLabel = UILabel().then { + $0.font = .bodyBody2 + $0.textColor = 0x2f2f2f.color + $0.numberOfLines = 0 + } + + private let descriptionLabel = UILabel().then { + $0.font = .bodyCaption + $0.textColor = 0x2f2f2f.color + $0.numberOfLines = 0 + } + private let priceLabel = UILabel().then { + $0.font = .bodyCaption2 + $0.textColor = 0x2f2f2f.color + $0.numberOfLines = 0 + } + private let wonLabel = UILabel().then { + $0.font = .bodyCaption2 + $0.textColor = 0x2f2f2f.color + $0.text = "원" + } + + private let discountLabel = UILabel().then { + $0.font = .bodyBody2 + $0.textColor = 0xf87171.color + } + + //MARK: - Identifier + static let identifier = "ProductCollectionViewCell" + + //MARK: - Life Cycles + override init(frame: CGRect) { + super.init(frame: frame) + layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +//MARK: - Extensions +extension relateCollectionViewCell { + + //MARK: - Layout Helpers + private func layout() { + contentView.addSubviews( + productImageView, + productLabel, + descriptionLabel, + priceLabel, + wonLabel, + discountLabel + ) + + productImageView.snp.makeConstraints { + $0.top.leading.trailing.equalToSuperview() + $0.height.equalTo(120) + } + + productLabel.snp.makeConstraints { + $0.top.equalTo(productImageView.snp.bottom).offset(8) + $0.leading.equalToSuperview() + } + + descriptionLabel.snp.makeConstraints { + $0.top.equalTo(productLabel.snp.bottom).offset(8) + $0.leading.equalToSuperview() + $0.trailing.equalToSuperview().offset(-5) + } + + priceLabel.snp.makeConstraints { + $0.top.equalTo(descriptionLabel.snp.bottom).offset(8) + $0.leading.equalToSuperview() + } + + wonLabel.snp.makeConstraints { + $0.top.equalTo(priceLabel) + $0.leading.equalTo(priceLabel.snp.trailing) + } + + discountLabel.snp.makeConstraints { + $0.centerY.equalTo(priceLabel) + $0.trailing.equalToSuperview().offset(-1) + } + } + + //MARK: - General Helpers + + func dataBind(model: relateModel) { + productImageView.image = UIImage(named: model.productImage) + productLabel.text = model.name + descriptionLabel.text = model.description + priceLabel.text = model.price + discountLabel.text = model.discountRate + } + +}