diff --git a/VDS/Components/Table/Table.swift b/VDS/Components/Table/Table.swift index a7f89a7d..3c290a80 100644 --- a/VDS/Components/Table/Table.swift +++ b/VDS/Components/Table/Table.swift @@ -9,13 +9,15 @@ import Foundation import UIKit import VDSTokens +///Table is view composed of rows and columns, which takes any view into each cell and resizes based on the highest cell height. @objc(VDSTable) open class Table: View { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - + + /// CollectionView to show the rows and columns private lazy var matrixView = SelfSizingCollectionView(frame: .zero, collectionViewLayout: flowLayout).with { $0.register(TableCellItem.self, forCellWithReuseIdentifier: TableCellItem.Identifier) $0.dataSource = self @@ -29,10 +31,12 @@ open class Table: View { $0.backgroundColor = .clear } + /// Custom flow layout to manage the height of the cells private lazy var flowLayout = MatrixFlowLayout().with { $0.delegate = self } + /// Array of ``TableItemModel`` by combining Header & Row items private var tableData: [[TableItemModel]] { return tableHeader + tableRows } @@ -41,6 +45,7 @@ open class Table: View { // MARK: - Enums //-------------------------------------------------- + /// Enums used to define the padding for the cell edge spacing. public enum Padding: String, CaseIterable { case standard, compact @@ -67,34 +72,55 @@ open class Table: View { // MARK: - Public Properties //-------------------------------------------------- + /// Parameter to set striped status for the table open var striped: Bool = false { didSet { setNeedsUpdate() } } + /// Parameter to set the padding for the cell open var padding: Padding = .standard { didSet { setNeedsUpdate() } } + /// Parameter to show the table header row open var tableHeader: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } } + /// Parameter to show the all table rows open var tableRows: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- + ///Called upon initializing the table view open override func initialSetup() { super.initialSetup() addSubview(matrixView) matrixView.pinToSuperView() } + /// Will update the table view, when called becasue of any changes in component parameters open override func updateView() { super.updateView() flowLayout.layoutPadding = padding matrixView.reloadData() matrixView.collectionViewLayout.invalidateLayout() } + + /// Resets to default settings. + open override func reset() { + super.reset() + striped = false + padding = .standard + tableHeader = [] + tableRows = [] + setNeedsUpdate() + } } extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableCollectionViewLayoutDataDelegate { + //-------------------------------------------------- + // MARK: - UICollectionViewDelegate & UICollectionViewDataSource + //-------------------------------------------------- + public func numberOfSections(in collectionView: UICollectionView) -> Int { return tableData.count } @@ -110,6 +136,10 @@ extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableColl return cell } + //-------------------------------------------------- + // MARK: - TableCollectionViewLayoutDataDelegate + //-------------------------------------------------- + func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel { return tableData[indexPath.section][indexPath.row] } diff --git a/VDS/Components/Table/TableCellItem.swift b/VDS/Components/Table/TableCellItem.swift index 989e8eef..1366b0c7 100644 --- a/VDS/Components/Table/TableCellItem.swift +++ b/VDS/Components/Table/TableCellItem.swift @@ -11,17 +11,31 @@ import VDSTokens final class TableCellItem: UICollectionViewCell { + /// Identifier for TableCellItem static let Identifier: String = String(describing: TableCellItem.self) + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + + /// Main view which holds the content of the cell private let containerView = View() + /// Line seperator for cell private let separator: Line = Line() + /// Color configuration for default background color private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) + + /// Color configuration for striped background color private let stripedColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) + /// Padding parameter to maintain the edge spacing of the containerView private var padding: Table.Padding = .standard + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- override init(frame: CGRect) { super.init(frame: frame) setupCell() @@ -39,7 +53,12 @@ final class TableCellItem: UICollectionViewCell { containerView.pinToSuperView() } - func updateCell(content: TableItemModel, surface: Surface, striped: Bool = false, padding: Table.Padding = .standard) { + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + + /// Updates the cell content with ``TableItemModel`` and styling/padding attributes from other parameters + public func updateCell(content: TableItemModel, surface: Surface, striped: Bool = false, padding: Table.Padding = .standard) { containerView.subviews.forEach({ $0.removeFromSuperview() }) self.padding = padding diff --git a/VDS/Components/Table/TableCellModel.swift b/VDS/Components/Table/TableCellModel.swift index 82e33500..5253c67f 100644 --- a/VDS/Components/Table/TableCellModel.swift +++ b/VDS/Components/Table/TableCellModel.swift @@ -9,16 +9,20 @@ import Foundation import UIKit 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 public struct TableItemModel { + /// Default cell height public var defaultHeight: CGFloat { return 50.0 } public var bottomLine: Line.Style? + /// Component to be show in the Table cell public var component: UIView & ComponentHeight public init(bottomLine: Line.Style? = nil, component: UIView & ComponentHeight) { @@ -27,6 +31,7 @@ public struct TableItemModel { } } +/// Confirming protocol ``ComponentHeight`` to determine the height, based on the width extension Label: ComponentHeight { public func estimatedHeight(for width: CGFloat) -> CGFloat? { diff --git a/VDS/Components/Table/TableFlowLayout.swift b/VDS/Components/Table/TableFlowLayout.swift index a3d8e817..c66fa89e 100644 --- a/VDS/Components/Table/TableFlowLayout.swift +++ b/VDS/Components/Table/TableFlowLayout.swift @@ -14,14 +14,33 @@ protocol TableCollectionViewLayoutDataDelegate: AnyObject { 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 - var itemCache: [UICollectionViewLayoutAttributes] = [] - - var layoutHeight: CGFloat = 0.0 + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + /// Calculates the layout attribute properties & total height of the collectionView override func prepare() { super.prepare() @@ -35,18 +54,22 @@ class MatrixFlowLayout : UICollectionViewLayout { var yPos: CGFloat = 0.0 + ///Looping through all the sections of the collectionView, visually these are rows for currentSection in 0.. $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() - VDSLayout.space1X - let height = item.component.estimatedHeight(for: itemWidth) ?? 0 + 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 { @@ -89,15 +116,18 @@ class MatrixFlowLayout : UICollectionViewLayout { } 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