vds_ios/VDS/Components/Table/TableFlowLayout.swift

122 lines
4.4 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) -> TableCellModel
}
class MatrixFlowLayout : UICollectionViewLayout {
weak var delegate: TableCollectionViewLayoutDataDelegate?
var layoutPadding: Table.Padding = .standard
var itemCache: [UICollectionViewLayoutAttributes] = []
var layoutHeight: CGFloat = 0.0
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
for currentSection in 0..<sections {
let items = collectionView.numberOfItems(inSection: currentSection)
for currentItem in 0..<items {
let indexPath = IndexPath(row: currentItem, section: currentSection)
let itemWidth = floor(collectionView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(items))
let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath)
let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth, padding: layoutPadding, isHeader: currentSection == 0)
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)
}
let highestHeightForSection = itemCache.sorted(by: {$0.frame.size.height > $1.frame.size.height }).first?.frame.size.height ?? 0.0
itemCache.filter({$0.indexPath.section == currentSection}).forEach { attributes in
attributes.frame.size.height = highestHeightForSection
}
yPos += highestHeightForSection
}
layoutHeight = yPos
}
private func estimateHeightFor(item: TableCellModel, with width: CGFloat, padding: Table.Padding, isHeader: Bool) -> CGFloat {
if let model = item as? Table.TableCellLabelModel {
return estimatedHeightForLabel(item: model, with: width, padding: padding, isHeader: isHeader)
}
return (item.defaultHeight + (2 * padding.verticalValue()))
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in itemCache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return itemCache.filter({ $0.indexPath == indexPath}).first
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: layoutHeight)
}
private var contentWidth: CGFloat {
guard let collectionView = collectionView else { return 0 }
return collectionView.bounds.width
}
}
extension MatrixFlowLayout {
private func estimatedHeightForLabel(item: Table.TableCellLabelModel, with width: CGFloat, padding: Table.Padding, isHeader: Bool) -> CGFloat {
let cellLabel = Label()
cellLabel.textStyle = isHeader ? .boldTitleSmall : UIDevice.isIPad ? .bodyLarge : .bodySmall
cellLabel.text = item.text
let labelWidth = width - padding.horizontalValue() - VDSLayout.space1X
let maxbounds = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)
let labelSize = cellLabel.sizeThatFits(maxbounds)
return labelSize.height + (2 * padding.verticalValue())
}
}