// // TableFlowLayout.swift // VDS // // Created by Nadigadda, Sumanth on 02/05/24. // import UIKit import VDSTokens protocol TableCollectionViewLayoutDataDelegate: AnyObject { func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel } class MatrixFlowLayout : UICollectionViewLayout { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- ///Spacing between the pagination cells private let defaultLeadingPadding: CGFloat = VDSLayout.space1X /// Parameter to store the layout attributes of cell, while calculate the size & position of the cell private var itemCache: [UICollectionViewLayoutAttributes] = [] /// Parameter to store the total height of the collectionView private var layoutHeight: CGFloat = 0.0 //-------------------------------------------------- // MARK: - Internal Properties //-------------------------------------------------- weak var delegate: TableCollectionViewLayoutDataDelegate? ///padding type to be set from Table component, which is used to calculate the size & position of the cell. var layoutPadding: Table.Padding = .standard //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- /// Calculates the layout attribute properties & total height of the collectionView override func prepare() { super.prepare() itemCache.removeAll() layoutHeight = 0.0 guard let collectionView, let delegate else { return } let sections = collectionView.numberOfSections var yPos: CGFloat = 0.0 ///Looping through all the sections of the collectionView, visually these are rows for currentSection in 0.. $1.frame.size.height }).first?.frame.size.height ?? 0.0 ///Set the highest height as height to all the cells in the row to make the row in uniform height. itemCache.filter({$0.indexPath.section == currentSection}).forEach { attributes in attributes.frame.size.height = highestHeightForSection } ///Adds the height to y position for the next section yPos += highestHeightForSection } layoutHeight = yPos } /// Fetches estimated height by calling the cell's component estimated height and adding padding private func estimateHeightFor(item: TableItemModel, with width: CGFloat) -> CGFloat { let itemWidth = width - layoutPadding.horizontalValue() - defaultLeadingPadding let height = item.component.estimatedHeight(for: itemWidth) ?? item.defaultHeight return height + (2 * layoutPadding.verticalValue()) } ///This will return the layout attributes for the elements in the defined rect override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = [] for attributes in itemCache { if attributes.frame.intersects(rect) { visibleLayoutAttributes.append(attributes) } } return visibleLayoutAttributes } ///This will return the layout attributes at particular indexPath override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { return itemCache.filter({ $0.indexPath == indexPath}).first } ///Returns the collectionview content size override var collectionViewContentSize: CGSize { return CGSize(width: contentWidth, height: layoutHeight) } /// Returns the width collectionView width private var contentWidth: CGFloat { guard let collectionView = collectionView else { return 0 } return collectionView.bounds.width } }