122 lines
4.4 KiB
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())
|
|
}
|
|
}
|