Skip to content

Instantly share code, notes, and snippets.

@AlexandrFadeev
Created June 30, 2021 07:16
Show Gist options
  • Save AlexandrFadeev/957c2dee1b781d915add97a9a2bfc682 to your computer and use it in GitHub Desktop.
Save AlexandrFadeev/957c2dee1b781d915add97a9a2bfc682 to your computer and use it in GitHub Desktop.
Product details in Bambinifasion project
import UIKit
let prevImageIconName = "prevImageIcon"
let nextImageIconName = "nextImageIcon"
let favoriteIconName = "favoriteIcon"
let productDetailsViewImageSliderWidthKoef: CGFloat = 0.96
protocol ProductDetailsViewDelegate: AnyObject {
func productDetailsView(_ productDetailsView: ProductDetailsView, didTouchUpInsideSizesButton: UITextField)
func productDetailsViewDidTouchUpInsideFavoriteButton(_ productDetailsView: ProductDetailsView)
func productDetailsViewDidTouchUpInsideAddToBagButton(_ productDetailsView: ProductDetailsView)
func productDetailsViewDidTouchUpInsideDescriptionCellButton(_ productDetailsView: ProductDetailsView)
func productDetailsViewDidTouchUpInsideDeliveryAndReturnsCellButton(_ productDetailsView: ProductDetailsView)
func productDetailsView(_ productDetailsView: ProductDetailsView,
didSelectSlideAt index: Int,
from images: [UIImage])
}
class ProductDetailsView: UIView {
weak var delegate: ProductDetailsViewDelegate?
var rootScrollView: UIScrollView!
var imageSlider: ImageSlider!
var titleLabel: UILabel!
var regularPriceLabel: UILabel!
var reducePriceLabel: UILabel!
var colorAndSizesLabel: UILabel!
var colorAndSizesLineView: UIView!
var colorsCollectionView: UICollectionView!
var sizesButton: AuthTextField!
var favoriteButton: SolidButton!
var addToBagButton: SolidButton!
var arrivalInfoLabel: UILabel!
var arrivalLineView: UIView!
var descriptionCellButton: CellButton!
var deliveryAndReturnsCellButton: CellButton!
var alsoLikeLabel: UILabel!
var alsoLikeLineView: UIView!
var alsoLikeCollectionView: UICollectionView!
var recentlyLabel: UILabel!
var recentlyLineView: UIView!
var recentlyCollectionView: UICollectionView!
var productDetails: ProductDetails? {
didSet {
imageSlider.items = productDetails?.imageUrls ?? []
}
}
var productSize: ProductSize? {
didSet {
sizesButton.text = productSize?.title
setNeedsLayout()
layoutIfNeeded()
}
}
override init(frame: CGRect) {
super.init(frame: .zero)
createSubviews()
configureView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureView() {
backgroundColor = .white
}
}
// MARK: - Creating and layout subviews
extension ProductDetailsView {
func createSubviews() {
createRootScrollView()
createImageSlider()
createTitleLabel()
createRegularPriceLabel()
createReducePriceLabel()
createColorAndSizesLabel()
createColorAndSizesLineView()
createColorsCollectionView()
createSizesButton()
createFavoriteButton()
createAddToBagButton()
createArrivalInfoLabel()
createArrivalLineView()
createDescriptionCellButton()
createDeliveryAndReturnsCellButton()
createAlsoLikeLabel()
createAlsoLikeLineView()
createAlsoLikeCollectionView()
createRecentlyLabel()
createRecentlyLineView()
createRecentlyCollectionView()
}
func createRootScrollView() {
rootScrollView = UIScrollView()
if #available(iOS 11, *) {
rootScrollView.contentInsetAdjustmentBehavior = .never
}
rootScrollView.showsVerticalScrollIndicator = false
rootScrollView.showsHorizontalScrollIndicator = false
rootScrollView.keyboardDismissMode = .onDrag
addSubview(rootScrollView)
}
func createImageSlider() {
imageSlider = ImageSlider(frame: .zero)
imageSlider.delegate = self
rootScrollView.addSubview(imageSlider)
}
func createTitleLabel() {
titleLabel = UILabel(frame: .zero)
titleLabel.font = .textLightFont
titleLabel.textColor = .darkGrayColor
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
rootScrollView.addSubview(titleLabel)
}
func createRegularPriceLabel() {
regularPriceLabel = UILabel(frame: .zero)
regularPriceLabel.backgroundColor = .white
regularPriceLabel.font = .titleTextBoldFont
regularPriceLabel.textColor = .mediumGrayColor
regularPriceLabel.textAlignment = .center
rootScrollView.addSubview(regularPriceLabel)
}
func createReducePriceLabel() {
reducePriceLabel = UILabel(frame: .zero)
reducePriceLabel.backgroundColor = .white
reducePriceLabel.font = .titleTextBoldFont
reducePriceLabel.textColor = .redTextColor
reducePriceLabel.textAlignment = .center
rootScrollView.addSubview(reducePriceLabel)
}
func createColorAndSizesLabel() {
colorAndSizesLabel = UILabel(frame: .zero)
colorAndSizesLabel.text = ShopLocalizedString("productDetailsView.colorAndSizesLabel.title").uppercased()
colorAndSizesLabel.font = .normaTextLightFont
colorAndSizesLabel.textColor = .mediumGrayColor
colorAndSizesLabel.textAlignment = .left
colorAndSizesLabel.backgroundColor = .clear
rootScrollView.addSubview(colorAndSizesLabel)
}
func createColorAndSizesLineView() {
colorAndSizesLineView = UIView(frame: .zero)
colorAndSizesLineView.backgroundColor = .lightGrayColor
rootScrollView.addSubview(colorAndSizesLineView)
}
func createColorsCollectionView() {
let collectionViewFlowLayout = UICollectionViewFlowLayout()
let sectionInset = UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 24)
collectionViewFlowLayout.minimumLineSpacing = 14
//collectionViewFlowLayout.minimumInteritemSpacing = 14
collectionViewFlowLayout.sectionInset = sectionInset
collectionViewFlowLayout.scrollDirection = .horizontal
colorsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
colorsCollectionView.showsHorizontalScrollIndicator = false
colorsCollectionView.backgroundColor = .clear
rootScrollView.addSubview(colorsCollectionView)
}
func createSizesButton() {
sizesButton = AuthTextField()
sizesButton.delegate = self
sizesButton.type = .selector
rootScrollView.addSubview(sizesButton)
}
func createFavoriteButton() {
favoriteButton = SolidButton(buttonType: .system)
favoriteButton.backgroundColor = .lightGrayColor
favoriteButton.setTitleColor(.darkGrayColor, for: .normal)
let favoriteImage = UIImage(named: favoriteIconName)
favoriteButton.setImage(favoriteImage, for: .normal)
favoriteButton.addTarget(self, action: #selector(didTouchUpInsideFavoriteButton(_:)), for: .touchUpInside)
//favoriteButton.isBorderEnabled = true
rootScrollView.addSubview(favoriteButton)
}
func createAddToBagButton() {
addToBagButton = SolidButton(buttonType: .system)
let title = ShopLocalizedString("productDetailsView.addToBagButton.title").uppercased()
addToBagButton.setTitle(title, for: .normal)
addToBagButton.addTarget(self, action: #selector(didTouchUpInsideAddToBagButton(_:)), for: .touchUpInside)
rootScrollView.addSubview(addToBagButton)
}
func createArrivalInfoLabel() {
arrivalInfoLabel = UILabel(frame: .zero)
arrivalInfoLabel.font = .textLightFont
arrivalInfoLabel.textColor = .darkGrayColor
arrivalInfoLabel.textAlignment = .center
arrivalInfoLabel.numberOfLines = 0
arrivalInfoLabel.attributedText = NSMutableAttributedString()
.normal("Estimate arrival to ")
.bold("Austria: \n")
.bold("Express: ")
.normal("Wednesday, July 29 \n")
.bold("Standard: ")
.normal("Friday, July 31")
arrivalInfoLabel.setLineHeight(lineHeight: lineMultiplier)
rootScrollView.addSubview(arrivalInfoLabel)
}
func createArrivalLineView() {
arrivalLineView = UIView(frame: .zero)
arrivalLineView.backgroundColor = .lightGray2Color
rootScrollView.addSubview(arrivalLineView)
}
func createDescriptionCellButton() {
descriptionCellButton = CellButton(frame: .zero)
descriptionCellButton.isDisplayLine = true
let title = ShopLocalizedString("productDetailsView.descriptionCellButton.title").uppercased()
descriptionCellButton.setTitle(title, for: .normal)
descriptionCellButton.contentHorizontalAlignment = .left
descriptionCellButton.addTarget(self, action: #selector(didTouchUpInsideDescriptionCellButton(_:)), for: .touchUpInside)
rootScrollView.addSubview(descriptionCellButton)
}
func createDeliveryAndReturnsCellButton() {
deliveryAndReturnsCellButton = CellButton(frame: .zero)
deliveryAndReturnsCellButton.isDisplayLine = true
let title = ShopLocalizedString("productDetailsView.deliveryAndReturnsCellButton.title").uppercased()
deliveryAndReturnsCellButton.setTitle(title, for: .normal)
deliveryAndReturnsCellButton.contentHorizontalAlignment = .left
deliveryAndReturnsCellButton.addTarget(self, action: #selector(didTouchUpInsideDeliveryAndReturnsCellButton(_:)), for: .touchUpInside)
rootScrollView.addSubview(deliveryAndReturnsCellButton)
}
func createAlsoLikeLabel() {
alsoLikeLabel = UILabel(frame: .zero)
alsoLikeLabel.text = ShopLocalizedString("productDetailsView.alsoLikeLabel.title").uppercased()
alsoLikeLabel.font = .normaTextLightFont
alsoLikeLabel.textColor = .mediumGrayColor
alsoLikeLabel.textAlignment = .left
alsoLikeLabel.backgroundColor = .clear
rootScrollView.addSubview(alsoLikeLabel)
}
func createAlsoLikeLineView() {
alsoLikeLineView = UIView(frame: .zero)
alsoLikeLineView.backgroundColor = .lightGray2Color
rootScrollView.addSubview(alsoLikeLineView)
}
func createAlsoLikeCollectionView() {
let collectionViewFlowLayout = UICollectionViewFlowLayout()
let sectionInset = UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 24)
collectionViewFlowLayout.minimumLineSpacing = 14
//collectionViewFlowLayout.minimumInteritemSpacing = 14
collectionViewFlowLayout.sectionInset = sectionInset
collectionViewFlowLayout.scrollDirection = .horizontal
alsoLikeCollectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
alsoLikeCollectionView.showsHorizontalScrollIndicator = false
alsoLikeCollectionView.backgroundColor = .clear
rootScrollView.addSubview(alsoLikeCollectionView)
}
func createRecentlyLabel() {
recentlyLabel = UILabel(frame: .zero)
recentlyLabel.text = ShopLocalizedString("productDetailsView.recentlyLabel.title").uppercased()
recentlyLabel.font = .normaTextLightFont
recentlyLabel.textColor = .mediumGrayColor
recentlyLabel.textAlignment = .left
recentlyLabel.backgroundColor = .clear
rootScrollView.addSubview(recentlyLabel)
}
func createRecentlyLineView() {
recentlyLineView = UIView(frame: .zero)
recentlyLineView.backgroundColor = .lightGray2Color
rootScrollView.addSubview(recentlyLineView)
}
func createRecentlyCollectionView() {
let collectionViewFlowLayout = UICollectionViewFlowLayout()
let sectionInset = UIEdgeInsets(top: 0, left: 24, bottom: 0, right: 24)
collectionViewFlowLayout.minimumLineSpacing = 14
//collectionViewFlowLayout.minimumInteritemSpacing = 14
collectionViewFlowLayout.sectionInset = sectionInset
collectionViewFlowLayout.scrollDirection = .horizontal
recentlyCollectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
recentlyCollectionView.showsHorizontalScrollIndicator = false
recentlyCollectionView.backgroundColor = .clear
rootScrollView.addSubview(recentlyCollectionView)
}
override func layoutSubviews() {
super.layoutSubviews()
layoutRootScrollView()
layoutImageSlider()
layoutTitleLabel()
layoutRegularPriceLabel()
layoutReducePriceLabel()
layoutColorAndSizesLabel()
layoutColorAndSizesLineView()
layoutColorsCollectionView()
layoutSizesButton()
layoutFavoriteButton()
layoutAddToBagButton()
layoutArrivalInfoLabel()
layoutArrivalLineView()
layoutDescriptionCellButton()
layoutDeliveryAndReturnsCellButton()
layoutAlsoLikeLabel()
layoutAlsoLikeLineView()
layoutAlsoLikeCollectionView()
layoutRecentlyLabel()
layoutRecentlyLineView()
layoutRecentlyCollectionView()
updateRootScrollViewContentSize()
}
func layoutRootScrollView() {
var frame = CGRect.zero
frame.size.width = self.frame.width
var height: CGFloat = self.frame.height
if #available(iOS 11, *) {
height -= safeAreaInsets.bottom
}
frame.size.height = height
rootScrollView.frame = frame
}
func layoutImageSlider() {
var frame = CGRect.zero
frame.size.width = self.frame.width
frame.size.height = frame.width * productDetailsViewImageSliderWidthKoef
frame.origin.x = (self.frame.width - frame.width) / 2
imageSlider.frame = frame
}
func layoutTitleLabel() {
titleLabel.sizeToFit()
var frame = titleLabel.frame
frame.size.width = imageSlider.frame.width
frame.origin.y = imageSlider.frame.maxY
frame.origin.x = (self.frame.width - frame.width) / 2
titleLabel.frame = frame
}
func layoutRegularPriceLabel() {
regularPriceLabel.sizeToFit()
var frame = regularPriceLabel.frame
frame.origin.y = titleLabel.frame.maxY + indent * 2
frame.origin.x = self.frame.width / 2 - frame.width - indent / 2
regularPriceLabel.frame = frame
}
func layoutReducePriceLabel() {
reducePriceLabel.sizeToFit()
var frame = reducePriceLabel.frame
frame.origin.y = regularPriceLabel.frame.minY
frame.origin.x = self.frame.width / 2 + indent / 2
reducePriceLabel.frame = frame
}
func layoutColorAndSizesLabel() {
var frame = CGRect.zero
frame.size.width = self.frame.width - CGFloat(buttonHorizontalIndent * 2)
frame.size.height = colorAndSizesLabel.sizeThatFits(CGSize(width: frame.width, height: CGFloat.greatestFiniteMagnitude)).height
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = regularPriceLabel.frame.maxY + largeVerticalInset
colorAndSizesLabel.frame = frame
}
func layoutColorAndSizesLineView() {
var frame = colorAndSizesLineView.frame
frame.size.height = 0.5
frame.size.width = colorAndSizesLabel.frame.width
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = colorAndSizesLabel.frame.maxY + smallVerticalInset
colorAndSizesLineView.frame = frame
}
func layoutColorsCollectionView() {
var frame = colorsCollectionView.frame
frame.origin.y = colorAndSizesLineView.frame.maxY + CGFloat(buttonVerticalIndent)
frame.size.width = self.frame.width
frame.size.height = sectionHeaderHeight
frame.origin.x = (self.frame.width - frame.width) / 2
colorsCollectionView.frame = frame
}
func layoutSizesButton() {
sizesButton.sizeToFit()
var frame = sizesButton.frame
frame.size.width = colorAndSizesLabel.frame.width
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = colorsCollectionView.frame.maxY + CGFloat(buttonVerticalIndent)
sizesButton.frame = frame
}
func layoutFavoriteButton() {
favoriteButton.sizeToFit()
var frame = favoriteButton.frame
frame.size.width = favoriteButton.frame.width + 36
frame.origin.x = sizesButton.frame.maxX - frame.width
frame.origin.y = sizesButton.frame.maxY + CGFloat(buttonVerticalIndent) * 2
favoriteButton.frame = frame
}
func layoutAddToBagButton() {
addToBagButton.sizeToFit()
var frame = addToBagButton.frame
frame.size.width = favoriteButton.frame.minX - sizesButton.frame.minX - 18
frame.origin.x = sizesButton.frame.minX
frame.origin.y = favoriteButton.frame.minY
addToBagButton.frame = frame
}
func layoutArrivalInfoLabel() {
var frame = CGRect.zero
frame.size.width = self.frame.width - CGFloat(buttonHorizontalIndent * 2)
frame.size.height = arrivalInfoLabel.sizeThatFits(CGSize(width: frame.width, height: CGFloat.greatestFiniteMagnitude)).height
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = addToBagButton.frame.maxY + 35
arrivalInfoLabel.frame = frame
}
func layoutArrivalLineView() {
var frame = arrivalLineView.frame
frame.origin.y = arrivalInfoLabel.frame.maxY + 46
frame.size.width = self.frame.width - CGFloat(buttonHorizontalIndent * 2)
frame.size.height = 0.5
frame.origin.x = (self.frame.width - frame.width) / 2
arrivalLineView.frame = frame
}
func layoutDescriptionCellButton() {
descriptionCellButton.sizeToFit()
var frame = descriptionCellButton.frame
frame.size.width = self.frame.width - CGFloat(buttonHorizontalIndent * 2)
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = arrivalLineView.frame.maxY
descriptionCellButton.frame = frame
}
func layoutDeliveryAndReturnsCellButton() {
deliveryAndReturnsCellButton.sizeToFit()
var frame = deliveryAndReturnsCellButton.frame
frame.size.width = descriptionCellButton.frame.width
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = descriptionCellButton.frame.maxY
deliveryAndReturnsCellButton.frame = frame
}
func layoutAlsoLikeLabel () {
var frame = CGRect.zero
frame.size.width = self.frame.width - CGFloat(buttonHorizontalIndent * 2)
frame.size.height = alsoLikeLabel.sizeThatFits(CGSize(width: frame.width, height: CGFloat.greatestFiniteMagnitude)).height
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = deliveryAndReturnsCellButton.frame.maxY + largeVerticalInset
alsoLikeLabel.frame = frame
}
func layoutAlsoLikeLineView() {
var frame = alsoLikeLineView.frame
frame.size.height = 0.5
frame.size.width = colorAndSizesLabel.frame.width
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = alsoLikeLabel.frame.maxY + indent
alsoLikeLineView.frame = frame
}
func layoutAlsoLikeCollectionView() {
var frame = alsoLikeCollectionView.frame
frame.origin.y = alsoLikeLineView.frame.maxY + CGFloat(buttonVerticalIndent)
frame.size.width = self.frame.width
frame.size.height = 182 * 1.5
frame.origin.x = (self.frame.width - frame.width) / 2
alsoLikeCollectionView.frame = frame
}
func layoutRecentlyLabel() {
var frame = CGRect.zero
frame.size.width = self.frame.width - CGFloat(buttonHorizontalIndent * 2)
frame.size.height = recentlyLabel.sizeThatFits(CGSize(width: frame.width, height: CGFloat.greatestFiniteMagnitude)).height
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = alsoLikeCollectionView.frame.maxY + largeVerticalInset
recentlyLabel.frame = frame
}
func layoutRecentlyLineView() {
var frame = recentlyLineView.frame
frame.size.height = 0.5
frame.size.width = colorAndSizesLabel.frame.width
frame.origin.x = (self.frame.width - frame.width) / 2
frame.origin.y = recentlyLabel.frame.maxY + indent
recentlyLineView.frame = frame
}
func layoutRecentlyCollectionView() {
var frame = recentlyCollectionView.frame
frame.origin.y = recentlyLineView.frame.maxY + CGFloat(buttonVerticalIndent)
frame.size.width = self.frame.width
frame.size.height = 182 * 1.5
frame.origin.x = (self.frame.width - frame.width) / 2
recentlyCollectionView.frame = frame
}
func updateRootScrollViewContentSize() {
rootScrollView.contentSize = CGSize(width: rootScrollView.frame.width, height: rootScrollView.maxHeight() + indent * 3)
}
}
// MARK: - Actions
extension ProductDetailsView {
@IBAction func didTouchUpInsideFavoriteButton(_ button: UIButton) {
delegate?.productDetailsViewDidTouchUpInsideFavoriteButton(self)
}
@IBAction func didTouchUpInsideAddToBagButton(_ button: UIButton) {
delegate?.productDetailsViewDidTouchUpInsideAddToBagButton(self)
}
@IBAction func didTouchUpInsideDescriptionCellButton(_ button: UIButton) {
delegate?.productDetailsViewDidTouchUpInsideDescriptionCellButton(self)
}
@IBAction func didTouchUpInsideDeliveryAndReturnsCellButton(_ button: UIButton) {
delegate?.productDetailsViewDidTouchUpInsideDeliveryAndReturnsCellButton(self)
}
}
// MARK: - UITextFieldDelegate
extension ProductDetailsView: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == sizesButton {
delegate?.productDetailsView(self, didTouchUpInsideSizesButton: textField)
}
self.endEditing(true)
return false
}
}
extension ProductDetailsView: ImageSliderDelegate {
func imageSlider(_ imageSliderView: ImageSlider, didSelectSlideAt index: Int, from images: [UIImage]) {
delegate?.productDetailsView(self, didSelectSlideAt: index, from: images)
}
func imageSlider(_ imageSliderView: ImageSlider, didFinishDownloadImages images: [UIImage]) {
titleLabel.text = productDetails?.title
setNeedsLayout()
layoutIfNeeded()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment