// // RadioSwatchGroup.swift // VDS // // Created by Matt Bruce on 8/25/22. // import Foundation import UIKit public class RadioSwatchGroup: RadioSwatchGroupBase { public override func didSelect(selector: DefaultRadioSwatchModel) { //reset the old model //see if there is a selected one and then get the cached version if let selectedModel { let oldSelectedModel = selectedModel.copyWith { $0.selected = false } replace(viewModel: oldSelectedModel) } //set the new model let newSelectedModel = selector.copyWith { $0.selected = true } label.text = newSelectedModel.text replace(viewModel: newSelectedModel) selectedInputId = newSelectedModel.inputId } } public class RadioSwatchGroupBase: Control, Changable, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate where GroupModelType.SelectorModelType == ModelHandlerType.ModelType { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @Proxy(\.model.selectedInputId) public var selectedInputId: String? { didSet { sendActions(for: .valueChanged) } } public var selectedModel: ModelHandlerType.ModelType? { guard let selectedInputId else { return nil } if let index = model.selectors.firstIndex(where: { element in return element.inputId == selectedInputId }) { return model.selectors[index] } else { return nil } } public var onChange: Blocks.ActionBlock? //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- public var label = Label() private let cellSize: CGFloat = 48.0 private let lineSpacing: CGFloat = 12.0 private let itemSpacing: CGFloat = 16.0 private 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.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() isTappable = false 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), collectionView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 24), collectionView.leadingAnchor.constraint(equalTo: leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: trailingAnchor), collectionView.bottomAnchor.constraint(equalTo: bottomAnchor), collectionView.heightAnchor.constraint(greaterThanOrEqualToConstant: cellSize), collectionView.widthAnchor.constraint(greaterThanOrEqualToConstant: cellSize * 10) ]) } open override func shouldUpdateView(viewModel: ModelType) -> Bool { return viewModel != model } public override func initialSetup() { super.initialSetup() collectionView.delegate = self collectionView.dataSource = self } open override func updateView(viewModel: ModelType) { label.set(with: viewModel.labelModel) collectionView.reloadData() setNeedsLayout() } //Refactor into new CollectionView Selector protocol public func updateSelectors(){ let selectors = model.selectors.compactMap { existing in return existing.copyWith { $0.disabled = disabled $0.surface = surface } } model.selectors = selectors } public func replace(viewModel: ModelHandlerType.ModelType){ if let index = model.selectors.firstIndex(where: { element in return element.inputId == viewModel.inputId }) { model.selectors[index] = viewModel } } public func getCachedSelector(viewModel: ModelHandlerType.ModelType) -> ModelHandlerType.ModelType? { if let index = model.selectors.firstIndex(where: { element in return element.inputId == viewModel.inputId }) { return model.selectors[index] } else { return nil } } //-------------------------------------------------- // 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 !model.selectors[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.model) } open func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell else { return } cell.isSelected = false } //-------------------------------------------------- // MARK: - UICollectionViewDataSource //-------------------------------------------------- public func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return model.selectors.count } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell let model = model.selectors[indexPath.row] cell?.modelHandler.isUserInteractionEnabled = false cell?.set(with: model) return cell ?? UICollectionViewCell() } open func didSelect(selector: ModelHandlerType.ModelType) { //override to do functionality } }