Changes to accommodate any ui element into table cell and make its size dynamic.

This commit is contained in:
Sumanth Nadigadda 2024-05-24 20:49:58 +05:30
parent d4957e5aec
commit 7748a5eae4
3 changed files with 39 additions and 40 deletions

View File

@ -24,7 +24,6 @@ open class Table: View {
$0.delegate = self $0.delegate = self
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
$0.allowsSelection = false $0.allowsSelection = false
$0.isScrollEnabled = false
$0.showsVerticalScrollIndicator = false $0.showsVerticalScrollIndicator = false
$0.showsHorizontalScrollIndicator = false $0.showsHorizontalScrollIndicator = false
$0.isAccessibilityElement = true $0.isAccessibilityElement = true
@ -34,6 +33,7 @@ open class Table: View {
/// Custom flow layout to manage the height of the cells /// Custom flow layout to manage the height of the cells
private lazy var flowLayout = MatrixFlowLayout().with { private lazy var flowLayout = MatrixFlowLayout().with {
$0.delegate = self $0.delegate = self
$0.scrollDirection = .horizontal
} }
/// Array of ``TableItemModel`` by combining Header & Row items /// Array of ``TableItemModel`` by combining Header & Row items
@ -84,6 +84,9 @@ open class Table: View {
/// Parameter to show the all table rows /// Parameter to show the all table rows
open var tableRows: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } } open var tableRows: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } }
open var fillContainer: Bool = true { didSet { setNeedsUpdate() } }
open var columnWidths: [CGFloat]? { didSet { setNeedsUpdate() } }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
@ -99,6 +102,9 @@ open class Table: View {
/// Will update the table view, when called becasue of any changes in component parameters /// Will update the table view, when called becasue of any changes in component parameters
open override func updateView() { open override func updateView() {
super.updateView() super.updateView()
if fillContainer == true || (fillContainer == false && columnWidths == nil) {
columnWidths = calculateColumnWidths()
}
flowLayout.layoutPadding = padding flowLayout.layoutPadding = padding
matrixView.reloadData() matrixView.reloadData()
matrixView.collectionViewLayout.invalidateLayout() matrixView.collectionViewLayout.invalidateLayout()
@ -111,8 +117,17 @@ open class Table: View {
padding = .standard padding = .standard
tableHeader = [] tableHeader = []
tableRows = [] tableRows = []
fillContainer = true
columnWidths = nil
setNeedsUpdate() setNeedsUpdate()
} }
func calculateColumnWidths() -> [CGFloat] {
guard let noOfColumns = tableData.first?.count else { return [] }
let itemWidth = floor(matrixView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(noOfColumns))
return Array(repeating: itemWidth, count: noOfColumns)
}
} }
extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableCollectionViewLayoutDataDelegate { extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableCollectionViewLayoutDataDelegate {
@ -143,4 +158,8 @@ extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableColl
func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel { func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel {
return tableData[indexPath.section][indexPath.row] return tableData[indexPath.section][indexPath.row]
} }
func collectionView(_ collectionView: UICollectionView, widthForItemAt indexPath: IndexPath) -> CGFloat {
return columnWidths?[indexPath.row] ?? 0.0
}
} }

View File

@ -9,40 +9,16 @@ import Foundation
import UIKit import UIKit
import VDSTokens import VDSTokens
/// Fetches the estimated height of component for said width
public protocol ComponentHeight {
func estimatedHeight(for width: CGFloat) -> CGFloat?
}
/// Model that represent the content of each cell of Table component /// Model that represent the content of each cell of Table component
public struct TableItemModel { public struct TableItemModel {
/// Default cell height
public var defaultHeight: CGFloat { return 50.0 }
public var bottomLine: Line.Style? public var bottomLine: Line.Style?
/// Component to be show in the Table cell /// Component to be show in the Table cell
public var component: UIView & ComponentHeight public var component: UIView
public init(bottomLine: Line.Style? = nil, component: UIView & ComponentHeight) { public init(bottomLine: Line.Style? = nil, component: UIView) {
self.bottomLine = bottomLine self.bottomLine = bottomLine
self.component = component self.component = component
} }
} }
/// Confirming protocol ``ComponentHeight`` to determine the height, based on the width
extension Label: ComponentHeight {
public func estimatedHeight(for width: CGFloat) -> CGFloat? {
let maxbounds = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
return self.sizeThatFits(maxbounds).height
}
}
extension Icon: ComponentHeight {
public func estimatedHeight(for width: CGFloat) -> CGFloat? {
return intrinsicContentSize.height
}
}

View File

@ -10,9 +10,10 @@ import VDSTokens
protocol TableCollectionViewLayoutDataDelegate: AnyObject { protocol TableCollectionViewLayoutDataDelegate: AnyObject {
func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel
func collectionView(_ collectionView: UICollectionView, widthForItemAt indexPath: IndexPath) -> CGFloat
} }
class MatrixFlowLayout : UICollectionViewLayout { class MatrixFlowLayout : UICollectionViewFlowLayout {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
@ -27,6 +28,9 @@ class MatrixFlowLayout : UICollectionViewLayout {
/// Parameter to store the total height of the collectionView /// Parameter to store the total height of the collectionView
private var layoutHeight: CGFloat = 0.0 private var layoutHeight: CGFloat = 0.0
/// Parameter to store the total width of the collectionView
private var layoutWidth: CGFloat = 0.0
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Internal Properties // MARK: - Internal Properties
//-------------------------------------------------- //--------------------------------------------------
@ -57,6 +61,9 @@ class MatrixFlowLayout : UICollectionViewLayout {
///Looping through all the sections of the collectionView, visually these are rows ///Looping through all the sections of the collectionView, visually these are rows
for currentSection in 0..<sections { for currentSection in 0..<sections {
/// Reset the layout width after each row's calculation
layoutWidth = 0.0
let items = collectionView.numberOfItems(inSection: currentSection) let items = collectionView.numberOfItems(inSection: currentSection)
///Looping through all the items in section, visually these are each column in the row ///Looping through all the items in section, visually these are each column in the row
@ -65,13 +72,15 @@ class MatrixFlowLayout : UICollectionViewLayout {
let indexPath = IndexPath(row: currentItem, section: currentSection) 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 /// 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 itemWidth = delegate.collectionView(collectionView, widthForItemAt: indexPath)
let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath) let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath)
///Calculate the estimated height of the cell ///Calculate the estimated height of the cell
let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth) let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth)
layoutWidth += itemWidth
let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath) let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let origin = CGPoint(x: itemWidth * CGFloat(indexPath.row), y: yPos) let origin = CGPoint(x: itemWidth * CGFloat(indexPath.row), y: yPos)
@ -84,7 +93,7 @@ class MatrixFlowLayout : UICollectionViewLayout {
} }
///Determines the highest height from all the cells(columns) in the row ///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 let highestHeightForSection = itemCache.filter({$0.indexPath.section == currentSection}).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. ///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 itemCache.filter({$0.indexPath.section == currentSection}).forEach { attributes in
@ -102,8 +111,9 @@ class MatrixFlowLayout : UICollectionViewLayout {
private func estimateHeightFor(item: TableItemModel, with width: CGFloat) -> CGFloat { private func estimateHeightFor(item: TableItemModel, with width: CGFloat) -> CGFloat {
let itemWidth = width - layoutPadding.horizontalValue() - defaultLeadingPadding let itemWidth = width - layoutPadding.horizontalValue() - defaultLeadingPadding
let height = item.component.estimatedHeight(for: itemWidth) ?? item.defaultHeight let maxSize = CGSize(width: itemWidth, height: CGFloat.greatestFiniteMagnitude)
return height + (2 * layoutPadding.verticalValue()) let estItemSize = item.component.systemLayoutSizeFitting(maxSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
return estItemSize.height + (2 * layoutPadding.verticalValue())
} }
///This will return the layout attributes for the elements in the defined rect ///This will return the layout attributes for the elements in the defined rect
@ -124,12 +134,6 @@ class MatrixFlowLayout : UICollectionViewLayout {
///Returns the collectionview content size ///Returns the collectionview content size
override var collectionViewContentSize: CGSize { override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: layoutHeight) return CGSize(width: layoutWidth, height: layoutHeight)
}
/// Returns the width collectionView width
private var contentWidth: CGFloat {
guard let collectionView = collectionView else { return 0 }
return collectionView.bounds.width
} }
} }