Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Created by https://www.toptal.com/developers/gitignore/api/swiftpackagemanager,swift,cocoapods,xcode
# Edit at https://www.toptal.com/developers/gitignore?templates=swiftpackagemanager,swift,cocoapods,xcode

### CocoaPods ###
## CocoaPods GitIgnore Template

# CocoaPods - Only use to conserve bandwidth / Save time on Pushing
# - Also handy if you have a large number of dependant pods
# - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
Pods/

### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
Expand Down Expand Up @@ -35,32 +47,26 @@ timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm

.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Expand All @@ -71,7 +77,6 @@ Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
Expand All @@ -83,8 +88,28 @@ fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/

### SwiftPackageManager ###
Packages
xcuserdata
*.xcodeproj


### Xcode ###

## Xcode 8 and earlier

### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings

# End of https://www.toptal.com/developers/gitignore/api/swiftpackagemanager,swift,cocoapods,xcode
86 changes: 67 additions & 19 deletions OpenMarket/OpenMarket.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions OpenMarket/OpenMarket/Controller/Cell/CellIdentifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// CellID.swift
// OpenMarket
//
// Created by 정연호 on 2022/11/10.
//

import Foundation

enum CellIdentifier {
static let listCellID: String = "ListPageCell"
static let gridCellID: String = "GridPageCell"
}
155 changes: 155 additions & 0 deletions OpenMarket/OpenMarket/Controller/Cell/GridPageCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//
// GridPageCell.swift
// OpenMarket
//
// Created by 정연호 on 2022/11/03.
//

import UIKit

final class GridPageCell: UICollectionViewCell, CellSelectable {
var productId: Int?

private lazy var thumbnailImage: UIImageView = UIImageView()

private lazy var productTitle: UILabel = {
let lable = UILabel()
lable.font = UIFont.preferredFont(forTextStyle: .headline)

return lable
}()

private lazy var productPriceLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.preferredFont(forTextStyle: .callout)
lable.textColor = .gray

return lable
}()

private lazy var productDiscountLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.preferredFont(forTextStyle: .callout)
lable.textColor = .gray

return lable
}()

private lazy var productStockLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.preferredFont(forTextStyle: .callout)

return lable
}()

private lazy var priceLabelStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [productPriceLabel, productDiscountLabel])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.alignment = .center
stackView.distribution = .equalSpacing
stackView.axis = .vertical
stackView.spacing = 1.0

return stackView
}()

private lazy var vStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [thumbnailImage, productTitle, priceLabelStackView, productStockLabel])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.alignment = .center
stackView.distribution = .equalSpacing
stackView.axis = .vertical

return stackView
}()

override init(frame: CGRect) {
super.init(frame: frame)
setCellLayer()
cellConstraint()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func prepareForReuse() {
super.prepareForReuse()
productPriceLabel.attributedText = nil
thumbnailImage.image = nil
}

private func setCellLayer() {
contentView.layer.borderColor = UIColor.gray.cgColor
contentView.layer.borderWidth = 1
contentView.layer.cornerRadius = 10
}

private func cellConstraint() {
let inset: CGFloat = 20

contentView.addSubview(vStackView)
NSLayoutConstraint.activate([
vStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: inset),
vStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -inset),
vStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
vStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
thumbnailImage.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8),
thumbnailImage.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.6)
])
}

func configure(page: Page) {
productId = page.id
productTitle.text = page.name
setThumbnailImage(thumbnail: page.thumbnail)
setPriceLabel(currency: page.currency, price: page.price, discountedPrice: page.discountedPrice)
setStockLabel(stock: page.stock)
}

private func setThumbnailImage(thumbnail: String) {
ImageCacheManager.loadImage(stringUrl: thumbnail) { image in
guard let thumbnailImage = image else { return }
DispatchQueue.main.async { [weak self] in
self?.thumbnailImage.image = thumbnailImage
}
}
}

private func setPriceLabel(currency: Currency, price: Double, discountedPrice: Double) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal

switch currency {
case .krw:
numberFormatter.maximumFractionDigits = 0
case .usd:
numberFormatter.maximumFractionDigits = 1
}

let priceString = numberFormatter.string(for: price) ?? ""
let discountedPriceString = numberFormatter.string(for: discountedPrice) ?? ""



if discountedPrice == 0 {
productDiscountLabel.isHidden = true
productPriceLabel.text = "\(currency.rawValue) \(priceString)"
} else {
productDiscountLabel.isHidden = false
let priceText = "\(currency.rawValue) \(priceString)"
productPriceLabel.attributedText = NSMutableAttributedString(allText: priceText, previousText: priceText)
productDiscountLabel.text = "\(currency.rawValue) \(discountedPriceString)"
}
}

private func setStockLabel(stock: Int) {
if stock == 0 {
productStockLabel.text = "품절"
productStockLabel.textColor = .orange
} else {
productStockLabel.text = "잔여수량 : \(stock)"
productStockLabel.textColor = .gray
}
}
}
131 changes: 131 additions & 0 deletions OpenMarket/OpenMarket/Controller/Cell/ListPageCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// PageCell.swift
// OpenMarket
//
// Created by 정재근 on 2022/11/01.
//

import UIKit

final class ListPageCell: UICollectionViewListCell, CellSelectable {
var productId: Int?
private let imageLength: CGFloat = 80
private var contentLayout: [NSLayoutConstraint]?
private lazy var listContentView = UIListContentView(configuration: .subtitleCell())
private lazy var thumbnailImage = UIImageView()
private let stockLabel: UILabel = {
let label = UILabel()
label.textAlignment = .right
label.numberOfLines = 2
label.font = .systemFont(ofSize: 15)

return label
}()

override init(frame: CGRect) {
super.init(frame: frame)
constraint()
}

override func prepareForReuse() {
super.prepareForReuse()
thumbnailImage.image = nil
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func listConfiguration() -> UIListContentConfiguration {
.subtitleCell()
}

private func constraint() {
guard contentLayout == nil else { return }
let indicator = UICellAccessory.disclosureIndicator()
self.accessories = [indicator]

[listContentView, stockLabel, thumbnailImage].forEach {
contentView.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}

let height = contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: imageLength)
height.priority = UILayoutPriority(999)

let layouts = [
height,
thumbnailImage.widthAnchor.constraint(equalToConstant: imageLength-5),
thumbnailImage.heightAnchor.constraint(equalToConstant: imageLength-5),
thumbnailImage.trailingAnchor.constraint(equalTo: listContentView.leadingAnchor),
thumbnailImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
thumbnailImage.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),

listContentView.topAnchor.constraint(equalTo: contentView.topAnchor),
listContentView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),

stockLabel.leadingAnchor.constraint(equalTo: listContentView.trailingAnchor),
stockLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5),
stockLabel.widthAnchor.constraint(equalTo: listContentView.widthAnchor, multiplier: 0.5),
stockLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15)
]
NSLayoutConstraint.activate(layouts)
}

func configureCell(page: Page) {
self.productId = page.id
setListContentView(name: page.name, currency: page.currency, price: page.price, discountedPrice: page.discountedPrice)
setThumbnailImage(thumbnail: page.thumbnail)
setStockLabel(stock: page.stock)
}

private func setListContentView(name: String, currency: Currency, price: Double, discountedPrice: Double) {
var configure = self.listConfiguration()

configure.text = name
configure.textProperties.font = .preferredFont(forTextStyle: .headline)
configure.secondaryTextProperties.font = .preferredFont(forTextStyle: .callout)
configure.secondaryTextProperties.color = .gray

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal

switch currency {
case .usd:
numberFormatter.maximumFractionDigits = 1
case .krw:
numberFormatter.maximumFractionDigits = 0
}

let priceString = numberFormatter.string(for: price) ?? ""

if discountedPrice == 0 {
configure.secondaryText = "\(currency.rawValue) \(priceString)"
} else {
let discountPrice = numberFormatter.string(for: discountedPrice) ?? ""
let priviousPrice = "\(currency.rawValue) \(priceString)"
let text = priviousPrice + " \(currency.rawValue) \(discountPrice)"
configure.secondaryAttributedText = NSMutableAttributedString(allText: text, previousText: priviousPrice)
}
listContentView.configuration = configure
}

private func setThumbnailImage(thumbnail: String) {
ImageCacheManager.loadImage(stringUrl: thumbnail) { image in
guard let thubnailImage = image else { return }
DispatchQueue.main.async { [weak self] in
self?.thumbnailImage.image = thubnailImage
}
}
}

private func setStockLabel(stock: Int) {
if stock == 0 {
stockLabel.text = "품절"
stockLabel.textColor = .orange
} else {
stockLabel.text = "잔여수량 : \(stock)"
stockLabel.textColor = .gray
}
}
}
Loading