Merge branch 'feature/buttonGroupUpdate' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into feature/textLink

# Conflicts:
#	VDS/Components/Buttons/TextLink/TextLink.swift
#	VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2022-12-01 11:56:39 -06:00
commit 6880d25f2b
5 changed files with 248 additions and 44 deletions

View File

@ -8,6 +8,7 @@
import Foundation import Foundation
import UIKit import UIKit
@objc(VDSSelfSizingCollectionView)
public final class SelfSizingCollectionView: UICollectionView { public final class SelfSizingCollectionView: UICollectionView {
private var contentSizeObservation: NSKeyValueObservation? private var contentSizeObservation: NSKeyValueObservation?

View File

@ -149,6 +149,8 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
cell.subviews.forEach { $0.removeFromSuperview() } cell.subviews.forEach { $0.removeFromSuperview() }
cell.addSubview(button) cell.addSubview(button)
button.pinToSuperView() button.pinToSuperView()
// cell.layer.borderColor = UIColor.black.cgColor
// cell.layer.borderWidth = 1
return cell return cell
} }
@ -156,11 +158,19 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
buttons[indexPath.row].intrinsicContentSize 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 { public func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets {
UIEdgeInsets.zero UIEdgeInsets.zero
} }
public func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat {
itemSpacing
}
} }

View File

@ -8,27 +8,55 @@
import Foundation import Foundation
import UIKit 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 { class ButtonCollectionViewRow {
var attributes = [UICollectionViewLayoutAttributes]() var attributes = [ButtonLayoutAttributes]()
var spacing: CGFloat = 0 var spacing: CGFloat = 0
init(spacing: CGFloat) { init(spacing: CGFloat) {
self.spacing = spacing self.spacing = spacing
} }
func add(attribute: UICollectionViewLayoutAttributes) { func add(attribute: ButtonLayoutAttributes) {
attributes.append(attribute) attributes.append(attribute)
} }
var hasButtons: Bool {
attributes.contains(where: { $0.isButton })
}
var rowWidth: CGFloat { var rowWidth: CGFloat {
return attributes.reduce(0, { result, attribute -> CGFloat in return attributes.reduce(0, { result, attribute -> CGFloat in
return result + attribute.frame.width return result + attribute.frame.width + attribute.spacing
}) + CGFloat(attributes.count - 1) * 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){ func layout(for position: ButtonPosition, with collectionViewWidth: CGFloat){
var offset = 0.0 var offset = 0.0
attributes.last?.spacing = 0
switch position { switch position {
case .left: case .left:
break break
@ -40,7 +68,7 @@ class ButtonCollectionViewRow {
for attribute in attributes { for attribute in attributes {
attribute.frame.origin.x = offset 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 { protocol ButtongGroupPositionLayoutDelegate: AnyObject {
func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize 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, insetsForItemsInSection section: Int) -> UIEdgeInsets
func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat
} }
class ButtonGroupPositionLayout: UICollectionViewLayout { class ButtonGroupPositionLayout: UICollectionViewLayout {
@ -62,58 +90,74 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
// Total height of the content. Will be used to configure the scrollview content // Total height of the content. Will be used to configure the scrollview content
var layoutHeight: CGFloat = 0.0 var layoutHeight: CGFloat = 0.0
var position: ButtonPosition = .left var position: ButtonPosition = .left
private var itemCache: [UICollectionViewLayoutAttributes] = [] private var itemCache: [ButtonLayoutAttributes] = []
override func prepare() { override func prepare() {
super.prepare() super.prepare()
let rowSpacingButton = 12.0
let rowSpacingTextLink = 12.0
itemCache.removeAll() itemCache.removeAll()
layoutHeight = 0.0 layoutHeight = 0.0
guard let collectionView = collectionView else { guard let collectionView, let delegate else {
return return
} }
var itemSpacing = 0.0 var itemSpacing = 0.0
// Variable to track the width of the layout at the current state when the item is being drawn // Variable to track the width of the layout at the current state when the item is being drawn
var layoutWidthIterator: CGFloat = 0.0 var layoutWidthIterator: CGFloat = 0.0
for section in 0..<collectionView.numberOfSections { let section = 0
// Get the necessary data (if implemented) from the delegates else provide default values
let insets: UIEdgeInsets = delegate.collectionView(collectionView, insetsForItemsInSection: section)
let rowSpacing: CGFloat = rowSpacingButton
// Variables to track individual item width and cumultative height of all items as they are being laid out.
var itemSize: CGSize = .zero
layoutHeight += insets.top
let totalItems = collectionView.numberOfItems(inSection: section)
for item in 0..<totalItems {
// Get the necessary data (if implemented) from the delegates else provide default values itemSpacing = 0.0
let insets: UIEdgeInsets = delegate?.collectionView(collectionView, insetsForItemsInSection: section) ?? UIEdgeInsets.zero
let interItemSpacing: CGFloat = delegate?.collectionView(collectionView, itemSpacingInSection: section) ?? 0.0
itemSpacing = interItemSpacing
// Variables to track individual item width and cumultative height of all items as they are being laid out.
var itemSize: CGSize = .zero
layoutHeight += insets.top let indexPath = IndexPath(item: item, section: section)
for item in 0..<collectionView.numberOfItems(inSection: section) { itemSize = delegate.collectionView(collectionView, sizeForItemAtIndexPath: indexPath)
let indexPath = IndexPath(item: item, section: section)
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width {
itemSize = delegate?.collectionView(collectionView, sizeForItemAtIndexPath: indexPath) ?? .zero // 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
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width { layoutHeight += itemSize.height + rowSpacing
// 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 itemButtonable = delegate.collectionView(collectionView, buttonableAtIndexPath: indexPath)
}
let nextItem = item + 1
let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height) if nextItem < totalItems {
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) let neighbor = delegate.collectionView(collectionView, buttonableAtIndexPath: IndexPath(item: nextItem, section: section))
attributes.frame = frame itemSpacing = getHorizontalSpacing(for: itemButtonable, neighboring: neighbor)
itemCache.append(attributes)
layoutWidthIterator = layoutWidthIterator + frame.width + interItemSpacing
} }
layoutHeight += itemSize.height + insets.bottom let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height)
layoutWidthIterator = 0.0 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 //Turn into rows and re-calculate
var rows = [ButtonCollectionViewRow]() var rows = [ButtonCollectionViewRow]()
var currentRowY: CGFloat = -1 var currentRowY: CGFloat = -1
for attribute in itemCache { for attribute in itemCache {
if currentRowY != attribute.frame.midY { if currentRowY != attribute.frame.midY {
currentRowY = attribute.frame.midY currentRowY = attribute.frame.midY
@ -121,12 +165,156 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
} }
rows.last?.add(attribute: attribute) rows.last?.add(attribute: attribute)
} }
//recalculate rows based off of positions //recalculate rows based off of positions
rows.forEach { $0.layout(for: position, with: collectionView.frame.width) } rows.forEach { $0.layout(for: position, with: collectionView.frame.width) }
let rowAttributes = rows.flatMap { $0.attributes } let rowAttributes = rows.flatMap { $0.attributes }
layoutHeight = insets.top
for item in 0..<rows.count {
let row = rows[item]
var rowSpacing = 0.0
if item > 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 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]? { override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? {

View File

@ -103,6 +103,11 @@ open class TextLink: ButtonBase {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
override open var intrinsicContentSize: CGSize {
var itemWidth = super.intrinsicContentSize.width
return CGSize(width: itemWidth, height: height)
}
open override func updateView() { open override func updateView() {
//need to set the properties so the super class //need to set the properties so the super class
//can render out the label correctly //can render out the label correctly

View File

@ -121,7 +121,7 @@ open class TextLinkCaret: ButtonBase {
open override func updateView() { open override func updateView() {
var updatedText = text ?? "" let updatedText = text ?? ""
caretView.surface = surface caretView.surface = surface
caretView.disabled = disabled caretView.disabled = disabled
caretView.direction = iconPosition == .right ? CaretView.Direction.right : CaretView.Direction.left caretView.direction = iconPosition == .right ? CaretView.Direction.right : CaretView.Direction.left