Adding comments to the Table components implementation.
This commit is contained in:
parent
0d421190a2
commit
d4957e5aec
@ -9,13 +9,15 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSTokens
|
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)
|
@objc(VDSTable)
|
||||||
open class Table: View {
|
open class Table: View {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// CollectionView to show the rows and columns
|
||||||
private lazy var matrixView = SelfSizingCollectionView(frame: .zero, collectionViewLayout: flowLayout).with {
|
private lazy var matrixView = SelfSizingCollectionView(frame: .zero, collectionViewLayout: flowLayout).with {
|
||||||
$0.register(TableCellItem.self, forCellWithReuseIdentifier: TableCellItem.Identifier)
|
$0.register(TableCellItem.self, forCellWithReuseIdentifier: TableCellItem.Identifier)
|
||||||
$0.dataSource = self
|
$0.dataSource = self
|
||||||
@ -29,10 +31,12 @@ open class Table: View {
|
|||||||
$0.backgroundColor = .clear
|
$0.backgroundColor = .clear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Array of ``TableItemModel`` by combining Header & Row items
|
||||||
private var tableData: [[TableItemModel]] {
|
private var tableData: [[TableItemModel]] {
|
||||||
return tableHeader + tableRows
|
return tableHeader + tableRows
|
||||||
}
|
}
|
||||||
@ -41,6 +45,7 @@ open class Table: View {
|
|||||||
// MARK: - Enums
|
// MARK: - Enums
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Enums used to define the padding for the cell edge spacing.
|
||||||
public enum Padding: String, CaseIterable {
|
public enum Padding: String, CaseIterable {
|
||||||
case standard, compact
|
case standard, compact
|
||||||
|
|
||||||
@ -67,34 +72,55 @@ open class Table: View {
|
|||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Parameter to set striped status for the table
|
||||||
open var striped: Bool = false { didSet { setNeedsUpdate() } }
|
open var striped: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Parameter to set the padding for the cell
|
||||||
open var padding: Padding = .standard { didSet { setNeedsUpdate() } }
|
open var padding: Padding = .standard { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Parameter to show the table header row
|
||||||
open var tableHeader: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } }
|
open var tableHeader: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
/// Parameter to show the all table rows
|
||||||
open var tableRows: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } }
|
open var tableRows: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
///Called upon initializing the table view
|
||||||
open override func initialSetup() {
|
open override func initialSetup() {
|
||||||
super.initialSetup()
|
super.initialSetup()
|
||||||
addSubview(matrixView)
|
addSubview(matrixView)
|
||||||
matrixView.pinToSuperView()
|
matrixView.pinToSuperView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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()
|
||||||
flowLayout.layoutPadding = padding
|
flowLayout.layoutPadding = padding
|
||||||
matrixView.reloadData()
|
matrixView.reloadData()
|
||||||
matrixView.collectionViewLayout.invalidateLayout()
|
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 {
|
extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableCollectionViewLayoutDataDelegate {
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - UICollectionViewDelegate & UICollectionViewDataSource
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public func numberOfSections(in collectionView: UICollectionView) -> Int {
|
public func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||||
return tableData.count
|
return tableData.count
|
||||||
}
|
}
|
||||||
@ -110,6 +136,10 @@ extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableColl
|
|||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - TableCollectionViewLayoutDataDelegate
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
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]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,17 +11,31 @@ import VDSTokens
|
|||||||
|
|
||||||
final class TableCellItem: UICollectionViewCell {
|
final class TableCellItem: UICollectionViewCell {
|
||||||
|
|
||||||
|
/// Identifier for TableCellItem
|
||||||
static let Identifier: String = String(describing: TableCellItem.self)
|
static let Identifier: String = String(describing: TableCellItem.self)
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Main view which holds the content of the cell
|
||||||
private let containerView = View()
|
private let containerView = View()
|
||||||
|
|
||||||
|
/// Line seperator for cell
|
||||||
private let separator: Line = Line()
|
private let separator: Line = Line()
|
||||||
|
|
||||||
|
/// Color configuration for default background color
|
||||||
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
|
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
|
||||||
|
|
||||||
|
/// Color configuration for striped background color
|
||||||
private let stripedColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark)
|
private let stripedColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark)
|
||||||
|
|
||||||
|
/// Padding parameter to maintain the edge spacing of the containerView
|
||||||
private var padding: Table.Padding = .standard
|
private var padding: Table.Padding = .standard
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializers
|
||||||
|
//--------------------------------------------------
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
setupCell()
|
setupCell()
|
||||||
@ -39,7 +53,12 @@ final class TableCellItem: UICollectionViewCell {
|
|||||||
containerView.pinToSuperView()
|
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() })
|
containerView.subviews.forEach({ $0.removeFromSuperview() })
|
||||||
self.padding = padding
|
self.padding = padding
|
||||||
|
|||||||
@ -9,16 +9,20 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSTokens
|
import VDSTokens
|
||||||
|
|
||||||
|
/// Fetches the estimated height of component for said width
|
||||||
public protocol ComponentHeight {
|
public protocol ComponentHeight {
|
||||||
func estimatedHeight(for width: CGFloat) -> CGFloat?
|
func estimatedHeight(for width: CGFloat) -> CGFloat?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 defaultHeight: CGFloat { return 50.0 }
|
||||||
|
|
||||||
public var bottomLine: Line.Style?
|
public var bottomLine: Line.Style?
|
||||||
|
|
||||||
|
/// Component to be show in the Table cell
|
||||||
public var component: UIView & ComponentHeight
|
public var component: UIView & ComponentHeight
|
||||||
|
|
||||||
public init(bottomLine: Line.Style? = nil, 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 {
|
extension Label: ComponentHeight {
|
||||||
|
|
||||||
public func estimatedHeight(for width: CGFloat) -> CGFloat? {
|
public func estimatedHeight(for width: CGFloat) -> CGFloat? {
|
||||||
|
|||||||
@ -14,14 +14,33 @@ protocol TableCollectionViewLayoutDataDelegate: AnyObject {
|
|||||||
|
|
||||||
class MatrixFlowLayout : UICollectionViewLayout {
|
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?
|
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 layoutPadding: Table.Padding = .standard
|
||||||
|
|
||||||
var itemCache: [UICollectionViewLayoutAttributes] = []
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
var layoutHeight: CGFloat = 0.0
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Calculates the layout attribute properties & total height of the collectionView
|
||||||
override func prepare() {
|
override func prepare() {
|
||||||
super.prepare()
|
super.prepare()
|
||||||
|
|
||||||
@ -35,18 +54,22 @@ class MatrixFlowLayout : UICollectionViewLayout {
|
|||||||
|
|
||||||
var yPos: CGFloat = 0.0
|
var yPos: CGFloat = 0.0
|
||||||
|
|
||||||
|
///Looping through all the sections of the collectionView, visually these are rows
|
||||||
for currentSection in 0..<sections {
|
for currentSection in 0..<sections {
|
||||||
|
|
||||||
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
|
||||||
for currentItem in 0..<items {
|
for currentItem in 0..<items {
|
||||||
|
|
||||||
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
|
||||||
let itemWidth = floor(collectionView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(items))
|
let itemWidth = floor(collectionView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(items))
|
||||||
|
|
||||||
let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath)
|
let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath)
|
||||||
|
|
||||||
|
///Calculate the estimated height of the cell
|
||||||
let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth)
|
let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth)
|
||||||
|
|
||||||
let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
||||||
@ -60,26 +83,30 @@ class MatrixFlowLayout : UICollectionViewLayout {
|
|||||||
itemCache.append(attribute)
|
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
|
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
|
itemCache.filter({$0.indexPath.section == currentSection}).forEach { attributes in
|
||||||
attributes.frame.size.height = highestHeightForSection
|
attributes.frame.size.height = highestHeightForSection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Adds the height to y position for the next section
|
||||||
yPos += highestHeightForSection
|
yPos += highestHeightForSection
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutHeight = yPos
|
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 {
|
private func estimateHeightFor(item: TableItemModel, with width: CGFloat) -> CGFloat {
|
||||||
|
|
||||||
let itemWidth = width - layoutPadding.horizontalValue() - VDSLayout.space1X
|
let itemWidth = width - layoutPadding.horizontalValue() - defaultLeadingPadding
|
||||||
let height = item.component.estimatedHeight(for: itemWidth) ?? 0
|
let height = item.component.estimatedHeight(for: itemWidth) ?? item.defaultHeight
|
||||||
return height + (2 * layoutPadding.verticalValue())
|
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]? {
|
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
||||||
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
|
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
|
||||||
for attributes in itemCache {
|
for attributes in itemCache {
|
||||||
@ -89,15 +116,18 @@ class MatrixFlowLayout : UICollectionViewLayout {
|
|||||||
}
|
}
|
||||||
return visibleLayoutAttributes
|
return visibleLayoutAttributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///This will return the layout attributes at particular indexPath
|
||||||
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
||||||
return itemCache.filter({ $0.indexPath == indexPath}).first
|
return itemCache.filter({ $0.indexPath == indexPath}).first
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Returns the collectionview content size
|
||||||
override var collectionViewContentSize: CGSize {
|
override var collectionViewContentSize: CGSize {
|
||||||
return CGSize(width: contentWidth, height: layoutHeight)
|
return CGSize(width: contentWidth, height: layoutHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the width collectionView width
|
||||||
private var contentWidth: CGFloat {
|
private var contentWidth: CGFloat {
|
||||||
guard let collectionView = collectionView else { return 0 }
|
guard let collectionView = collectionView else { return 0 }
|
||||||
return collectionView.bounds.width
|
return collectionView.bounds.width
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user