// // RadioSwatchGroup.swift // VDS // // Created by Matt Bruce on 8/25/22. // import Foundation import UIKit import Combine public class RadioSwatchGroup: RadioSwatchGroupBase { public override func didSelect(selector: RadioSwatch) { if let index = selectorViews.firstIndex(where: {$0.isSelected == true }), let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell { cell.modelHandler.toggle() } selector.toggle() label.text = selector.text valueChanged() } } public class RadioSwatchGroupBase: SelectorGroupSelectedHandlerBase, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- public override var selectorViews: [ModelHandlerType] { didSet { collectionView.reloadData() } } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- public var label = Label() private let cellSize: CGFloat = 48.0 private let labelSpacing: CGFloat = 24.0 private let labelHeight: CGFloat = 16.0 private let lineSpacing: CGFloat = 12.0 private let itemSpacing: CGFloat = 16.0 private var collectionViewHeight: NSLayoutConstraint? private var collectionViewWidth: NSLayoutConstraint? fileprivate lazy var collectionView: UICollectionView = { let layout = UICollectionViewFlowLayout().with { $0.minimumLineSpacing = lineSpacing $0.minimumInteritemSpacing = itemSpacing } return UICollectionView(frame: .zero, collectionViewLayout: layout).with { $0.backgroundColor = .clear $0.showsHorizontalScrollIndicator = false $0.showsVerticalScrollIndicator = false $0.isScrollEnabled = false $0.translatesAutoresizingMaskIntoConstraints = false $0.register(CollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") } }() //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- override public var disabled: Bool { didSet { updateSelectors() } } override public var surface: Surface { didSet { updateSelectors() } } open override func setup() { super.setup() isAccessibilityElement = true accessibilityTraits = .button addSubview(label) addSubview(collectionView) NSLayoutConstraint.activate([ label.topAnchor.constraint(equalTo: topAnchor), label.leadingAnchor.constraint(equalTo: leadingAnchor), label.trailingAnchor.constraint(equalTo: trailingAnchor), label.heightAnchor.constraint(equalToConstant: labelHeight), collectionView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: labelSpacing), collectionView.leadingAnchor.constraint(equalTo: leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: trailingAnchor), collectionView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) //TODO: Look at this width stuff, we should NOT need it! collectionViewWidth = collectionView.widthAnchor.constraint(equalToConstant: cellSize * 20) collectionViewWidth?.isActive = true collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: cellSize) collectionViewHeight?.isActive = true } open override func layoutSubviews() { super.layoutSubviews() // Accounts for any collection size changes setHeight() DispatchQueue.main.async { self.collectionView.collectionViewLayout.invalidateLayout() } } open func setHeight() { guard selectorViews.count > 0 else { collectionViewHeight?.constant = 0 return } // Calculate the height let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing))) let numberOfRows = ceil(CGFloat(selectorViews.count)/swatchesInRow) let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1)) collectionViewHeight?.constant = CGFloat(height) } public override func initialSetup() { super.initialSetup() collectionView.delegate = self collectionView.dataSource = self } open override func updateView() { label.textPosition = .left label.typograpicalStyle = .BodySmall label.text = selectedHandler?.text ?? " " label.surface = surface label.disabled = disabled collectionView.reloadData() } private func updateSelectors() { for selector in selectorViews { selector.surface = surface selector.disabled = disabled } } //-------------------------------------------------- // MARK: - UICollectionViewDelegateFlowLayout //-------------------------------------------------- open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: cellSize, height: cellSize) } //-------------------------------------------------- // MARK: - UICollectionViewDelegate //-------------------------------------------------- open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { return !selectorViews[indexPath.row].disabled } open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell else { return } didSelect(selector: cell.modelHandler) } //-------------------------------------------------- // MARK: - UICollectionViewDataSource //-------------------------------------------------- public func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return selectorViews.count } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell else { return UICollectionViewCell() } let model = selectorViews[indexPath.row] cell.modelHandler = selectorViews[indexPath.row] cell.modelHandler.isUserInteractionEnabled = false return cell } open func didSelect(selector: ModelHandlerType) { fatalError("Must override didSelect") } }