// // CalendarLegendView.swift // VDS // // Created by Kanamarlapudi, Vasavi on 29/04/24. // import Foundation import UIKit import VDSTokens import Combine /// Legend view to show array of indicators as calendar footer view. open class CalendarLegendView: View { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- required public init() { super.init(frame: .zero) } public override init(frame: CGRect) { super.init(frame: .zero) } public required init?(coder: NSCoder) { super.init(coder: coder) } //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- open var items: [CalendarIndicatorModel] = [] { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- internal var containerSize: CGSize { CGSize(width: 320, height: 56) } //width:320/328 internal var containerView = View().with { $0.clipsToBounds = true } private let flowLayout = UICollectionViewFlowLayout().with { $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize $0.minimumLineSpacing = VDSLayout.space1X $0.minimumInteritemSpacing = VDSLayout.space4X $0.scrollDirection = .vertical } open lazy var legendCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout).with { $0.isScrollEnabled = false $0.translatesAutoresizingMaskIntoConstraints = false $0.showsVerticalScrollIndicator = false $0.showsHorizontalScrollIndicator = false $0.isAccessibilityElement = true $0.backgroundColor = .clear $0.delegate = self $0.dataSource = self $0.register(LegendCollectionViewCell.self, forCellWithReuseIdentifier: LegendCollectionViewCell.identifier) } //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- open override func initialSetup() { super.initialSetup() } open override func setup() { super.setup() isAccessibilityElement = false addSubview(containerView) containerView .pinTop(VDSLayout.space6X) .pinBottom(VDSLayout.space4X) .pinLeadingGreaterThanOrEqualTo(leadingAnchor, VDSLayout.space5X, .defaultLow) .pinTrailingLessThanOrEqualTo(trailingAnchor, VDSLayout.space5X, .defaultHigh) .height(containerSize.height) .width(containerSize.width) containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate() // legend Collection View containerView.addSubview(legendCollectionView) legendCollectionView.pinToSuperView() } open override func updateView() { super.updateView() legendCollectionView.reloadData() } override open func layoutSubviews() { super.layoutSubviews() } open override func reset() { super.reset() } } extension CalendarLegendView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard collectionView == legendCollectionView, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LegendCollectionViewCell.identifier, for: indexPath) as? LegendCollectionViewCell, indexPath.row <= items.count else { return UICollectionViewCell() } let text = items[indexPath.row].label cell.updateTitle(text: text, color: VDSColor.elementsSecondaryOnlight, surface: surface, clearFullcircle: indexPath.row == 1, drawSemiCircle: indexPath.row == 2) return cell } } private class LegendCollectionViewCell: UICollectionViewCell { static let identifier: String = String(describing: LegendCollectionViewCell.self) private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) private let indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark) private var title = Label().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.textAlignment = .left $0.numberOfLines = 1 $0.textStyle = .bodySmall $0.backgroundColor = .clear $0.isAccessibilityElement = false } private var legendIndicatorWrapper: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear } private var legendIndicator: View = View().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear $0.layer.borderWidth = 1.0 } private lazy var stackView = UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.distribution = .equalSpacing $0.spacing = VDSLayout.space2X $0.axis = .horizontal $0.backgroundColor = .clear } private lazy var shapeLayer = CAShapeLayer() //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) setupCell() } required init?(coder: NSCoder) { super.init(coder: coder) setupCell() } func setupCell() { addSubview(stackView) stackView.pinToSuperView() legendIndicatorWrapper.addSubview(legendIndicator) legendIndicator.pinLeading().pinTrailing().width(8).height(8).pinCenterY() stackView.addArrangedSubview(legendIndicatorWrapper) stackView.addArrangedSubview(title) } func updateTitle(text: String, color: UIColor, surface: Surface, clearFullcircle: Bool, drawSemiCircle: Bool) { title.text = text title.textColor = textColorConfiguration.getColor(surface) legendIndicator.backgroundColor = drawSemiCircle ? .clear : (clearFullcircle ? .clear : color) legendIndicator.layer.borderColor = indicatorColorConfiguration.getColor(surface).cgColor self.layoutIfNeeded() legendIndicator.layer.cornerRadius = legendIndicator.frame.size.height / 2.0 guard drawSemiCircle else { return } let center = CGPoint(x: legendIndicator.frame.size.width/2, y: legendIndicator.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 legendIndicator.layer.sublayers?.contains(shapeLayer) ?? true else { return } legendIndicator.layer.addSublayer(shapeLayer) } }