diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index c9df4b12..9cc1431b 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -25,9 +25,7 @@ 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; }; 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; - 446209482BE8E3AF003EBC19 /* TableCellImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 446209472BE8E3AF003EBC19 /* TableCellImageModel.swift */; }; 44A952D92BE384C40009F874 /* TableCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A952D82BE384C40009F874 /* TableCellModel.swift */; }; - 44A952DB2BE3852E0009F874 /* TableCellLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A952DA2BE3852E0009F874 /* TableCellLabelModel.swift */; }; 44A952DD2BE3DA820009F874 /* TableFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A952DC2BE3DA820009F874 /* TableFlowLayout.swift */; }; 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; @@ -220,9 +218,7 @@ 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = ""; }; 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; - 446209472BE8E3AF003EBC19 /* TableCellImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableCellImageModel.swift; sourceTree = ""; }; 44A952D82BE384C40009F874 /* TableCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableCellModel.swift; sourceTree = ""; }; - 44A952DA2BE3852E0009F874 /* TableCellLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableCellLabelModel.swift; sourceTree = ""; }; 44A952DC2BE3DA820009F874 /* TableFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableFlowLayout.swift; sourceTree = ""; }; 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; @@ -446,8 +442,6 @@ 443DBAF92BDA303F0021497E /* TableCellItem.swift */, 44A952DC2BE3DA820009F874 /* TableFlowLayout.swift */, 44A952D82BE384C40009F874 /* TableCellModel.swift */, - 44A952DA2BE3852E0009F874 /* TableCellLabelModel.swift */, - 446209472BE8E3AF003EBC19 /* TableCellImageModel.swift */, ); path = Table; sourceTree = ""; @@ -1224,7 +1218,6 @@ EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */, EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* TextStyleLabelAttribute.swift in Sources */, - 44A952DB2BE3852E0009F874 /* TableCellLabelModel.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */, EA985BE629688F6A00F2FF2E /* TileletBadgeModel.swift in Sources */, @@ -1245,7 +1238,6 @@ EA0D1C392A6AD4DF00E5C127 /* Typography+SpacingConfig.swift in Sources */, EAB2376629E9952D00AABE9A /* UIApplication.swift in Sources */, EAB5FED429267EB300998C17 /* UIView+NSLayoutConstraint.swift in Sources */, - 446209482BE8E3AF003EBC19 /* TableCellImageModel.swift in Sources */, EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */, EA33623E2892EE950071C351 /* UIDevice.swift in Sources */, EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */, diff --git a/VDS/Components/Table/Table.swift b/VDS/Components/Table/Table.swift index 40b3e089..a7f89a7d 100644 --- a/VDS/Components/Table/Table.swift +++ b/VDS/Components/Table/Table.swift @@ -32,6 +32,10 @@ open class Table: View { private lazy var flowLayout = MatrixFlowLayout().with { $0.delegate = self } + + private var tableData: [[TableItemModel]] { + return tableHeader + tableRows + } //-------------------------------------------------- // MARK: - Enums @@ -67,15 +71,9 @@ open class Table: View { open var padding: Padding = .standard { didSet { setNeedsUpdate() } } - open var headerBottomLine: Bool = false { didSet { setNeedsUpdate() } } + open var tableHeader: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } } - open var rowBottomLine: Bool = false { didSet { setNeedsUpdate() } } - - open var headerBottomLineType: Line.Style = .primary { didSet { setNeedsUpdate() } } - - open var rowBottomLineType: Line.Style = .secondary { didSet { setNeedsUpdate() } } - - open var tableData: [[TableCellModel]] = [] { didSet { setNeedsUpdate() } } + open var tableRows: [[TableItemModel]] = [] { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Overrides @@ -108,13 +106,11 @@ extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableColl guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TableCellItem.Identifier, for: indexPath) as? TableCellItem else { return UICollectionViewCell() } let currentItem = tableData[indexPath.section][indexPath.row] let shouldStrip = striped ? (indexPath.section % 2 != 0) : false - let style = indexPath.section == 0 ? headerBottomLineType : rowBottomLineType - let hideSeparator = indexPath.section == 0 ? headerBottomLine : rowBottomLine - cell.updateCell(content: currentItem, surface: surface, separatorStyle: style, isHeader: indexPath.section == 0, hideSeparator: hideSeparator, striped: shouldStrip, padding: padding) + cell.updateCell(content: currentItem, surface: surface, striped: shouldStrip, padding: padding) return cell } - func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableCellModel { + func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel { return tableData[indexPath.section][indexPath.row] } } diff --git a/VDS/Components/Table/TableCellImageModel.swift b/VDS/Components/Table/TableCellImageModel.swift deleted file mode 100644 index 883315f3..00000000 --- a/VDS/Components/Table/TableCellImageModel.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// TableCellImageModel.swift -// VDS -// -// Created by Nadigadda, Sumanth on 06/05/24. -// - -import Foundation - -extension Table { - - public struct TableCellImageModel: TableCellModel, Surfaceable { - - public var defaultHeight: CGFloat { return 50.0 } - - public var name: Icon.Name - - public var size: Icon.Size - - public var surface: Surface - - public init(name: Icon.Name, size: Icon.Size, surface: Surface = .light) { - self.name = name - self.size = size - self.surface = surface - } - } -} diff --git a/VDS/Components/Table/TableCellItem.swift b/VDS/Components/Table/TableCellItem.swift index 1d1c9255..989e8eef 100644 --- a/VDS/Components/Table/TableCellItem.swift +++ b/VDS/Components/Table/TableCellItem.swift @@ -15,18 +15,6 @@ final class TableCellItem: UICollectionViewCell { private let containerView = View() - private var cellLabel = Label().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.setContentHuggingPriority(.defaultHigh, for: .horizontal) - $0.setContentHuggingPriority(.defaultHigh, for:.vertical) - $0.textAlignment = .left - $0.lineBreakMode = .byWordWrapping - } - - private var icon = Icon().with { - $0.size = UIDevice.isIPad ? .medium : .small - } - private let separator: Line = Line() private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) @@ -51,60 +39,32 @@ final class TableCellItem: UICollectionViewCell { containerView.pinToSuperView() } - func updateCell(content: TableCellModel, surface: Surface, separatorStyle: Line.Style, isHeader: Bool = false, hideSeparator: Bool = false, striped: Bool = false, padding: Table.Padding = .standard) { + func updateCell(content: TableItemModel, surface: Surface, striped: Bool = false, padding: Table.Padding = .standard) { containerView.subviews.forEach({ $0.removeFromSuperview() }) self.padding = padding containerView.surface = surface containerView.backgroundColor = striped ? stripedColorConfiguration.getColor(surface) : backgroundColorConfiguration.getColor(surface) - if let model = content as? Table.TableCellLabelModel { - addLabel(model: model, surface: surface, isHeader: isHeader, padding: padding) - } else if let model = content as? Table.TableCellImageModel { - addImage(model: model, surface: surface) + containerView.addSubview(content.component) + + if var surfacedView = content.component as? Surfaceable { + surfacedView.surface = surface } + NSLayoutConstraint.activate([ + content.component.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: VDSLayout.space1X), + content.component.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: padding.verticalValue()), + containerView.bottomAnchor.constraint(greaterThanOrEqualTo: content.component.bottomAnchor, constant: padding.verticalValue()), + containerView.trailingAnchor.constraint(greaterThanOrEqualTo: content.component.trailingAnchor, constant: padding.horizontalValue()), + containerView.centerYAnchor.constraint(equalTo: content.component.centerYAnchor) + ]) + containerView.addSubview(separator) separator.pinLeading().pinTrailing().pinBottom() - separator.isHidden = !hideSeparator - separator.style = separatorStyle + separator.style = content.bottomLine ?? .primary + separator.isHidden = content.bottomLine == nil separator.surface = surface } - - private func addLabel(model: Table.TableCellLabelModel, surface: Surface, isHeader: Bool, padding: Table.Padding) { - - containerView.addSubview(cellLabel) - cellLabel.pinLeading(VDSLayout.space1X) - NSLayoutConstraint.activate([ - cellLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: padding.verticalValue()), - containerView.bottomAnchor.constraint(equalTo: cellLabel.bottomAnchor, constant: padding.verticalValue()), - containerView.trailingAnchor.constraint(equalTo: cellLabel.trailingAnchor, constant: padding.horizontalValue()) - ]) - - cellLabel.textStyle = textStyle(for: isHeader) - cellLabel.text = model.text - cellLabel.surface = surface - } - - private func addImage(model: Table.TableCellImageModel, surface: Surface) { - containerView.addSubview(icon) - icon.pinLeading().pinCenterY() - - icon.name = model.name - icon.surface = surface - } - - private func textStyle(for header:Bool) -> TextStyle { - return header ? .boldTitleSmall : UIDevice.isIPad ? .bodyLarge : .bodySmall - } - -// override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { -// let labelWidth = layoutAttributes.frame.size.width - (2 * padding.horizontalValue()) -// let maxbounds = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude) -// let labelSize = cellLabel.textRect(forBounds: CGRect(origin: .zero, size: maxbounds), limitedToNumberOfLines: cellLabel.numberOfLines) -// var labelHeight = max(labelSize.height, layoutAttributes.frame.size.height) + (2 * padding.horizontalValue()) -// layoutAttributes.frame.size = CGSize(width: layoutAttributes.frame.size.width, height: labelHeight) -// return layoutAttributes -// } } diff --git a/VDS/Components/Table/TableCellLabelModel.swift b/VDS/Components/Table/TableCellLabelModel.swift deleted file mode 100644 index 2f2d4297..00000000 --- a/VDS/Components/Table/TableCellLabelModel.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// TableCellLabelModel.swift -// VDS -// -// Created by Nadigadda, Sumanth on 02/05/24. -// - -import Foundation - -extension Table { - public struct TableCellLabelModel: TableCellModel, Surfaceable { - - public var defaultHeight: CGFloat { return 50.0 } - - public var text: String - - public var accessibilityString: String? - - public var surface: Surface - - public init(text: String, accessibilityString: String? = "", surface: Surface = .light) { - self.text = text - self.accessibilityString = accessibilityString - self.surface = surface - } - } -} diff --git a/VDS/Components/Table/TableCellModel.swift b/VDS/Components/Table/TableCellModel.swift index 5e92ebe8..82e33500 100644 --- a/VDS/Components/Table/TableCellModel.swift +++ b/VDS/Components/Table/TableCellModel.swift @@ -6,7 +6,38 @@ // import Foundation +import UIKit +import VDSTokens -public protocol TableCellModel { - var defaultHeight: CGFloat { get } +public protocol ComponentHeight { + func estimatedHeight(for width: CGFloat) -> CGFloat? +} + +public struct TableItemModel { + + public var defaultHeight: CGFloat { return 50.0 } + + public var bottomLine: Line.Style? + + public var component: UIView & ComponentHeight + + public init(bottomLine: Line.Style? = nil, component: UIView & ComponentHeight) { + self.bottomLine = bottomLine + self.component = component + } +} + +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 + } } diff --git a/VDS/Components/Table/TableFlowLayout.swift b/VDS/Components/Table/TableFlowLayout.swift index 8b6f313b..a3d8e817 100644 --- a/VDS/Components/Table/TableFlowLayout.swift +++ b/VDS/Components/Table/TableFlowLayout.swift @@ -9,7 +9,7 @@ import UIKit import VDSTokens protocol TableCollectionViewLayoutDataDelegate: AnyObject { - func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableCellModel + func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel } class MatrixFlowLayout : UICollectionViewLayout { @@ -47,7 +47,7 @@ class MatrixFlowLayout : UICollectionViewLayout { let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath) - let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth, padding: layoutPadding, isHeader: currentSection == 0) + let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth) let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath) @@ -73,12 +73,11 @@ class MatrixFlowLayout : UICollectionViewLayout { } - private func estimateHeightFor(item: TableCellModel, with width: CGFloat, padding: Table.Padding, isHeader: Bool) -> CGFloat { + private func estimateHeightFor(item: TableItemModel, with width: CGFloat) -> CGFloat { - if let model = item as? Table.TableCellLabelModel { - return estimatedHeightForLabel(item: model, with: width, padding: padding, isHeader: isHeader) - } - return (item.defaultHeight + (2 * padding.verticalValue())) + let itemWidth = width - layoutPadding.horizontalValue() - VDSLayout.space1X + let height = item.component.estimatedHeight(for: itemWidth) ?? 0 + return height + (2 * layoutPadding.verticalValue()) } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { @@ -104,18 +103,3 @@ class MatrixFlowLayout : UICollectionViewLayout { 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()) - } -}