136 lines
5.5 KiB
Swift
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
|
|
}
|
|
}
|