diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index 69270d9a..55c3962b 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -8,6 +8,7 @@ import Foundation import UIKit +@objc(VDSSelfSizingCollectionView) public final class SelfSizingCollectionView: UICollectionView { private var contentSizeObservation: NSKeyValueObservation? diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index d15b34a3..28ddebd3 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -149,6 +149,8 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega cell.subviews.forEach { $0.removeFromSuperview() } cell.addSubview(button) button.pinToSuperView() +// cell.layer.borderColor = UIColor.black.cgColor +// cell.layer.borderWidth = 1 return cell } @@ -156,11 +158,19 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega buttons[indexPath.row].intrinsicContentSize } + public func collectionView(_ collectionView: UICollectionView, isButtonTypeForItemAtIndexPath indexPath: IndexPath) -> Bool { + if let _ = buttons[indexPath.row] as? Button { + return true + } else { + return false + } + } + + public func collectionView(_ collectionView: UICollectionView, buttonableAtIndexPath indexPath: IndexPath) -> Buttonable { + buttons[indexPath.row] + } + public func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets { UIEdgeInsets.zero } - - public func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat { - itemSpacing - } } diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 1a017135..a0f6cad4 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -8,27 +8,55 @@ import Foundation import UIKit +class ButtonLayoutAttributes: UICollectionViewLayoutAttributes{ + var spacing: CGFloat = 0 + var isButton: Bool = false + convenience init(spacing: CGFloat, + forCellWith indexPath: IndexPath) { + self.init(forCellWith: indexPath) + self.spacing = spacing + } +} + class ButtonCollectionViewRow { - var attributes = [UICollectionViewLayoutAttributes]() + var attributes = [ButtonLayoutAttributes]() var spacing: CGFloat = 0 init(spacing: CGFloat) { self.spacing = spacing } - func add(attribute: UICollectionViewLayoutAttributes) { + func add(attribute: ButtonLayoutAttributes) { attributes.append(attribute) } + + var hasButtons: Bool { + attributes.contains(where: { $0.isButton }) + } var rowWidth: CGFloat { return attributes.reduce(0, { result, attribute -> CGFloat in - return result + attribute.frame.width - }) + CGFloat(attributes.count - 1) * spacing + return result + attribute.frame.width + attribute.spacing + }) + } + + var rowHeight: CGFloat { + attributes.compactMap{$0.frame.height}.max() ?? 0 + } + + var rowY: CGFloat = 0 { + didSet { + for attribute in attributes { + attribute.frame.origin.y = rowY + } + } } func layout(for position: ButtonPosition, with collectionViewWidth: CGFloat){ var offset = 0.0 + attributes.last?.spacing = 0 + switch position { case .left: break @@ -40,7 +68,7 @@ class ButtonCollectionViewRow { for attribute in attributes { attribute.frame.origin.x = offset - offset += attribute.frame.width + spacing + offset += attribute.frame.width + attribute.spacing } } } @@ -51,8 +79,8 @@ public enum ButtonPosition: String, CaseIterable { protocol ButtongGroupPositionLayoutDelegate: AnyObject { func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize + func collectionView(_ collectionView: UICollectionView, buttonableAtIndexPath indexPath: IndexPath) -> any Buttonable func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets - func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat } class ButtonGroupPositionLayout: UICollectionViewLayout { @@ -62,58 +90,74 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { // Total height of the content. Will be used to configure the scrollview content var layoutHeight: CGFloat = 0.0 var position: ButtonPosition = .left - private var itemCache: [UICollectionViewLayoutAttributes] = [] + private var itemCache: [ButtonLayoutAttributes] = [] override func prepare() { super.prepare() - + + let rowSpacingButton = 12.0 + let rowSpacingTextLink = 12.0 + itemCache.removeAll() layoutHeight = 0.0 - - guard let collectionView = collectionView else { + + guard let collectionView, let delegate else { return } var itemSpacing = 0.0 // Variable to track the width of the layout at the current state when the item is being drawn var layoutWidthIterator: CGFloat = 0.0 - for section in 0.. collectionView.frame.width { - // If the current row width (after this item being laid out) is exceeding the width of the collection view content, put it in the next line - layoutWidthIterator = 0.0 - layoutHeight += itemSize.height + interItemSpacing - } - - let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height) - let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) - attributes.frame = frame - itemCache.append(attributes) - layoutWidthIterator = layoutWidthIterator + frame.width + interItemSpacing + itemSize = delegate.collectionView(collectionView, sizeForItemAtIndexPath: indexPath) + + if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width { + // If the current row width (after this item being laid out) is exceeding the width of the collection view content, put it in the next line + layoutWidthIterator = 0.0 + layoutHeight += itemSize.height + rowSpacing + } + + let itemButtonable = delegate.collectionView(collectionView, buttonableAtIndexPath: indexPath) + + let nextItem = item + 1 + if nextItem < totalItems { + let neighbor = delegate.collectionView(collectionView, buttonableAtIndexPath: IndexPath(item: nextItem, section: section)) + itemSpacing = getHorizontalSpacing(for: itemButtonable, neighboring: neighbor) } - layoutHeight += itemSize.height + insets.bottom - layoutWidthIterator = 0.0 + let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height) + print(frame) + let attributes = ButtonLayoutAttributes(spacing: itemSpacing, forCellWith: indexPath) + attributes.frame = frame + attributes.isButton = isButton(buttonable: itemButtonable) + itemCache.append(attributes) + + layoutWidthIterator = layoutWidthIterator + frame.width + itemSpacing } + print("*******") + layoutHeight += itemSize.height + insets.bottom + layoutWidthIterator = 0.0 + //Turn into rows and re-calculate var rows = [ButtonCollectionViewRow]() var currentRowY: CGFloat = -1 - + for attribute in itemCache { if currentRowY != attribute.frame.midY { currentRowY = attribute.frame.midY @@ -121,12 +165,156 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { } rows.last?.add(attribute: attribute) } - + //recalculate rows based off of positions rows.forEach { $0.layout(for: position, with: collectionView.frame.width) } let rowAttributes = rows.flatMap { $0.attributes } + + layoutHeight = insets.top + for item in 0.. 0 && item < rows.count { + rowSpacing = row.hasButtons ? rowSpacingButton : rowSpacingTextLink + } + + if item > 0 { + row.rowY = layoutHeight + rowSpacing + layoutHeight += rowSpacing + } + + layoutHeight += row.rowHeight + } + layoutHeight += insets.bottom + + itemCache = rowAttributes - + + } + + func isButton(buttonable: Buttonable) -> Bool{ + if let _ = buttonable as? Button { + return true + } else { + return false + } + } + + func getHorizontalSpacing(for primary: Buttonable, neighboring: Buttonable) -> CGFloat { + let defaultSpace = 12.0 + //large button + if let button = primary as? Button, button.size == .large { + if let neighboringButton = neighboring as? Button, neighboringButton.size == .large { + return 12.0 + } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { + return 16.0 + } else if let _ = neighboring as? TextLinkCaret { + return 24.0 + } else { + return defaultSpace + } + } + //large text link + else if let textLink = primary as? TextLink, textLink.size == .large { + if let neighboringButton = neighboring as? Button, neighboringButton.size == .large { + return 16.0 + } else if let _ = neighboring as? TextLinkCaret { + return 24.0 + } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { + return 16.0 + } else { + return defaultSpace + } + } + //text link caret + else if let _ = primary as? TextLinkCaret { + if let _ = neighboring as? TextLinkCaret { + return 24.0 + } else { + return defaultSpace + } + } + //small button + else if let button = primary as? Button, button.size == .small { + if let neighboringButton = neighboring as? Button, neighboringButton.size == .small { + return 12.0 + } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small { + return 16.0 + } else { + return defaultSpace + } + } + //small text link + else if let textLink = primary as? TextLink, textLink.size == .small { + if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small { + return 16.0 + } else { + return defaultSpace + } + } + //return defaultSpace + else { + return defaultSpace + } + } + + func getVerticalSpacing(for primary: Buttonable, neighboring: Buttonable) -> CGFloat { + let defaultSpace = 12.0 + //large button + if let button = primary as? Button, button.size == .large { + if let neighboringButton = neighboring as? Button, neighboringButton.size == .large { + return 12.0 + } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { + return 16.0 + } else if let _ = neighboring as? TextLinkCaret { + return 24.0 + } else { + return defaultSpace + } + } + //large text link + else if let textLink = primary as? TextLink, textLink.size == .large { + if let neighboringButton = neighboring as? Button, neighboringButton.size == .large { + return 16.0 + } else if let _ = neighboring as? TextLinkCaret { + return 24.0 + } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { + return 24.0 + } else { + return defaultSpace + } + } + //text link caret + else if let _ = primary as? TextLinkCaret { + if let _ = neighboring as? TextLinkCaret { + return 24.0 + } else { + return defaultSpace + } + } + //small button + else if let button = primary as? Button, button.size == .small { + if let neighboringButton = neighboring as? Button, neighboringButton.size == .small { + return 12.0 + } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small { + return 24.0 + } else { + return defaultSpace + } + } + //small text link + else if let textLink = primary as? TextLink, textLink.size == .small { + if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small { + return 32.0 + } else { + return defaultSpace + } + } + //return defaultSpace + else { + return defaultSpace + } } override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? { diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index 292e28ad..36e415ec 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -103,6 +103,11 @@ open class TextLink: ButtonBase { //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- + override open var intrinsicContentSize: CGSize { + var itemWidth = super.intrinsicContentSize.width + return CGSize(width: itemWidth, height: height) + } + open override func updateView() { //need to set the properties so the super class //can render out the label correctly diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index 7555095e..368e0c14 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -121,7 +121,7 @@ open class TextLinkCaret: ButtonBase { open override func updateView() { - var updatedText = text ?? "" + let updatedText = text ?? "" caretView.surface = surface caretView.disabled = disabled caretView.direction = iconPosition == .right ? CaretView.Direction.right : CaretView.Direction.left