// // CalendarDateCollectionViewCell.swift // VDS // // Created by Kanamarlapudi, Vasavi on 24/04/24. // import Foundation import UIKit import VDSTokens /// Calendar collection view for Date view final class CalendarDateCollectionViewCell: UICollectionViewCell { ///Identifier for the Calendar Date Cell static let identifier: String = String(describing: CalendarDateCollectionViewCell.self) //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 40, height: 40) } internal var containerView = View().with { $0.clipsToBounds = true } private lazy var stackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill $0.alignment = .center $0.spacing = VDSLayout.space1X $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) } }() private var numberLabel = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .center $0.textStyle = .bodySmall } private lazy var shapeLayer = CAShapeLayer() private let selectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryInverseOnlight, VDSColor.elementsPrimaryInverseOndark) private let selectedBackgroundColor = SurfaceColorConfiguration(VDSColor.backgroundPrimaryInverseLight, VDSColor.backgroundPrimaryInverseDark) private let selectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteGray65, VDSColor.paletteGray44) private let unselectedTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) private let unselectedCellIndicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) private let currentDate = Date() //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) setUp() } required init?(coder: NSCoder) { super.init(coder: coder) setUp() } private func setUp() { isAccessibilityElement = false contentView.addSubview(containerView) containerView .pinTop() .pinBottom() .pinLeadingGreaterThanOrEqualTo() .pinTrailingLessThanOrEqualTo() .height(containerSize.height) .width(containerSize.width) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() // Number label containerView.addSubview(numberLabel) numberLabel.pinToSuperView() // Indicators containerView.addSubview(stackView) let topPos = containerSize.height * 0.7 stackView.pinTop(topPos).pinBottom().pinTopGreaterThanOrEqualTo().pinTrailingLessThanOrEqualTo().pinCenterY() stackView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() } func update(with surface: Surface, indicators: [CalendarBase.CalendarIndicatorModel], text: String, indicatorCount: Int, selectedDate: Date, hideDate: Bool) { numberLabel.text = text numberLabel.surface = surface // numberLabel.isEnabled = isEnabled stackView.arrangedSubviews.forEach { $0.removeFromSuperview() } // update text color, bg color, corner radius if numberLabel.text == self.getDay(with: selectedDate) { numberLabel.textColor = selectedTextColorConfiguration.getColor(surface) layer.backgroundColor = selectedBackgroundColor.getColor(surface).cgColor layer.cornerRadius = VDSFormControls.borderRadius } else { numberLabel.textColor = unselectedTextColorConfiguration.getColor(surface) layer.backgroundColor = nil layer.cornerRadius = 0 } // add indicators if indicatorCount > 0 { let width = (indicatorCount * 8) + (Int(VDSLayout.space1X) * (indicatorCount - 1)) stackView.widthAnchor.constraint(equalToConstant: CGFloat(width)).activate() for x in (0...(indicators.count-1)) { if (self.numberLabel.text == self.getDay(with: indicators[x].date)) { if numberLabel.text == self.getDay(with: selectedDate) { addIndicator(with: selectedCellIndicatorColorConfiguration.getColor(surface), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) } else { addIndicator(with: unselectedCellIndicatorColorConfiguration.getColor(surface), surface: surface, clearFullCircle: (x == 1), drawSemiCircle: (x == 2)) } } } } // update text style for current date if (numberLabel.text == self.getDay(with: currentDate)) { numberLabel.textStyle = hideDate ? .bodySmall : .boldBodySmall } else { numberLabel.textStyle = .bodySmall } } //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- func addIndicator(with color: UIColor, surface: Surface, clearFullCircle: Bool, drawSemiCircle: Bool) { // add indicator let indicatorView: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear $0.layer.borderWidth = 1.0 } indicatorView.pinLeading().pinTrailing().width(8).height(8).pinCenterY() stackView.addArrangedSubview(indicatorView) // update indicator indicatorView.backgroundColor = drawSemiCircle ? .clear : (clearFullCircle ? .clear : color) indicatorView.layer.borderColor = color.cgColor self.layoutIfNeeded() indicatorView.layer.cornerRadius = indicatorView.frame.size.height / 2.0 guard drawSemiCircle else { return } let center = CGPoint(x: indicatorView.frame.size.width/2, y: indicatorView.frame.size.height/2) let path = UIBezierPath() path.move(to: center) path.addArc(withCenter: center, radius: center.x, startAngle: 2 * .pi, endAngle: .pi, clockwise: true) path.close() shapeLayer.path = path.cgPath shapeLayer.fillColor = color.cgColor guard indicatorView.layer.sublayers?.contains(shapeLayer) ?? true else { return } indicatorView.layer.addSublayer(shapeLayer) } func getDay(with date:Date) -> String { if #available(iOS 15.0, *) { return date.formatted(.dateTime.day()) } else { // Fallback on earlier versions let dateFormatter: DateFormatter = DateFormatter() dateFormatter.dateFormat = "d" let day: String = dateFormatter.string(from: date) return day } } }