// // TileletGroup.swift // VDS // // Created by Matt Bruce on 10/8/24. // import Foundation import UIKit import VDSCoreTokens import Combine open class TileletGroup: 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 //-------------------------------------------------- /// An object containing number of Button components per row for iPhones open var rowQuantityPhone: Int = 0 { didSet { setNeedsUpdate() } } /// An object containing number of Button components per row for iPads open var rowQuantityTablet: Int = 0 { didSet { setNeedsUpdate() } } /// An object containing number of Button components per row open var rowQuantity: Int { UIDevice.isIPad ? rowQuantityTablet : rowQuantityPhone } /// Array of Buttonable Views that are shown in the group. open var tilelets: [Tilelet] = [] { didSet { setNeedsUpdate() } } /// Whether this object is enabled or not override open var isEnabled: Bool { didSet { tilelets.forEach { $0.isEnabled = isEnabled } } } /// Current Surface and this is used to pass down to child objects that implement Surfacable override open var surface: Surface { didSet { tilelets.forEach { $0.surface = surface } } } open var contentSizePublisher: AnyPublisher { collectionView.contentSizePublisher } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- fileprivate lazy var positionLayout = TileletGroupPositionLayout().with { $0.delegate = self } /// CollectionView that renders the array of buttonBase obects. fileprivate lazy var collectionView: SelfSizingCollectionView = { return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with { $0.backgroundColor = .clear $0.showsHorizontalScrollIndicator = false $0.showsVerticalScrollIndicator = false $0.isScrollEnabled = false $0.translatesAutoresizingMaskIntoConstraints = false $0.dataSource = self $0.delegate = self $0.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") } }() //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() addSubview(collectionView) collectionView.pinToSuperView() } //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() positionLayout.rowQuantity = rowQuantity var width: CGFloat? tilelets.forEach { tilelet in if let width { tilelet.width = width } } collectionView.reloadData() } open override func setDefaults() { super.setDefaults() rowQuantityPhone = 0 rowQuantityTablet = 0 tilelets = [] } open override func reset() { tilelets.forEach { $0.reset() } super.reset() } open override func layoutSubviews() { super.layoutSubviews() // Accounts for any collection size changes DispatchQueue.main.async { [weak self] in guard let self else { return } self.collectionView.collectionViewLayout.invalidateLayout() } } } extension TileletGroup: UICollectionViewDataSource, UICollectionViewDelegate { public func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return tilelets.count } open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let tilelet = tilelets[indexPath.row] let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) cell.contentView.subviews.forEach { $0.removeFromSuperview() } cell.contentView.addSubview(tilelet) tilelet.pinToSuperView() if hasDebugBorder { cell.addDebugBorder() } else { cell.removeDebugBorder() } return cell } public func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize { tilelets[indexPath.row].intrinsicContentSize } } extension TileletGroup : TileletGroupPositionLayoutDelegate { func collectionView(_ collectionView: UICollectionView, tileletAtIndexPath indexPath: IndexPath) -> Tilelet { tilelets[indexPath.row] } }