vds_ios/VDS/Components/Table/TableFlowLayout.swift
2024-05-14 19:09:03 +05:30

136 lines
5.5 KiB
Swift

//
// 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..<sections {
let items = collectionView.numberOfItems(inSection: currentSection)
///Looping through all the items in section, visually these are each column in the row
for currentItem in 0..<items {
let indexPath = IndexPath(row: currentItem, section: currentSection)
/// Dividing the colletionView width by number of items(columns) in the row to determine width of each cell
let itemWidth = floor(collectionView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(items))
let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath)
///Calculate the estimated height of the cell
let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth)
let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let origin = CGPoint(x: itemWidth * CGFloat(indexPath.row), y: yPos)
let size = CGSize(width: itemWidth, height: itemHeight)
attribute.frame = CGRect(origin: origin, size: size)
itemCache.append(attribute)
}
///Determines the highest height from all the cells(columns) in the row
let highestHeightForSection = itemCache.sorted(by: {$0.frame.size.height > $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
}
}