From 1257112c0663fced18bda25d9414ba099209eaea Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 1 Dec 2022 13:53:38 -0600 Subject: [PATCH 1/2] moved out methods to constants Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 + .../ButtonGroup/ButtonGroupConstants.swift | 130 ++++++++++++++ .../ButtonGroupPositionLayout.swift | 168 +++--------------- 3 files changed, 161 insertions(+), 141 deletions(-) create mode 100644 VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 18741ce7..7351cf9e 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -62,6 +62,7 @@ EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */; }; EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */; }; EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF4292D371F00998C17 /* ButtonBase.swift */; }; + EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */; }; EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; }; EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; }; EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; }; @@ -155,6 +156,7 @@ EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupPositionLayout.swift; sourceTree = ""; }; EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfSizingCollectionView.swift; sourceTree = ""; }; EAB5FEF4292D371F00998C17 /* ButtonBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBase.swift; sourceTree = ""; }; + EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = ""; }; EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = ""; }; EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = ""; }; EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; @@ -226,6 +228,7 @@ children = ( EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */, EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */, + EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */, ); path = ButtonGroup; sourceTree = ""; @@ -680,6 +683,7 @@ EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */, EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, + EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */, EAF7F09E289AAEC000B287F5 /* Constants.swift in Sources */, diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift new file mode 100644 index 00000000..193f5723 --- /dev/null +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift @@ -0,0 +1,130 @@ +// +// ButtonGroupConstants.swift +// VDS +// +// Created by Matt Bruce on 12/1/22. +// + +import Foundation + +struct ButtonGroupConstants { + static let rowSpacingButton = 12.0 + static let rowSpacingTextLink = 12.0 + + static 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 + } + } + + static 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 + } + } + +} diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 64751ca2..a6ad235a 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -8,23 +8,11 @@ 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 = [ButtonLayoutAttributes]() - var spacing: CGFloat = 0 - init(spacing: CGFloat) { - self.spacing = spacing - } + init() {} func add(attribute: ButtonLayoutAttributes) { attributes.append(attribute) @@ -83,6 +71,16 @@ protocol ButtongGroupPositionLayoutDelegate: AnyObject { func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets } +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 ButtonGroupPositionLayout: UICollectionViewLayout { weak var delegate: ButtongGroupPositionLayoutDelegate? @@ -94,33 +92,36 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { override func prepare() { super.prepare() - - let rowSpacingButton = 12.0 - let rowSpacingTextLink = 12.0 - + itemCache.removeAll() layoutHeight = 0.0 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 + // Only 1 section in the ButtonGroup 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 + + let rowSpacing: CGFloat = ButtonGroupConstants.rowSpacingButton + // Variables to track individual item width and cumultative height of all items as they are being laid out. var itemSize: CGSize = .zero + // add top layoutHeight += insets.top + let totalItems = collectionView.numberOfItems(inSection: section) for item in 0.. 0 && item < rows.count { - rowSpacing = row.hasButtons ? rowSpacingButton : rowSpacingTextLink + rowSpacing = row.hasButtons ? ButtonGroupConstants.rowSpacingButton : ButtonGroupConstants.rowSpacingTextLink } if item > 0 { @@ -200,123 +202,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { 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]? { var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = [] From e3d2ca8ff290ad7ceccb28574fbe108ae269f919 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 1 Dec 2022 15:15:21 -0600 Subject: [PATCH 2/2] refactored button group position layout added in rowQuantity Signed-off-by: Matt Bruce --- .../Buttons/ButtonGroup/ButtonGroup.swift | 5 +- .../ButtonGroupPositionLayout.swift | 114 ++++++++++-------- 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 28ddebd3..cc7c54f4 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -121,6 +121,7 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega open override func updateView() { super.updateView() positionLayout.position = buttonPosition + positionLayout.rowQuantity = rowQuantity collectionView.reloadData() } @@ -169,8 +170,6 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega public func collectionView(_ collectionView: UICollectionView, buttonableAtIndexPath indexPath: IndexPath) -> Buttonable { buttons[indexPath.row] } + - public func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets { - UIEdgeInsets.zero - } } diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index a6ad235a..32bdab7b 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -11,7 +11,7 @@ import UIKit class ButtonCollectionViewRow { var attributes = [ButtonLayoutAttributes]() - + init() {} func add(attribute: ButtonLayoutAttributes) { @@ -49,9 +49,9 @@ class ButtonCollectionViewRow { case .left: break case .center: - offset = (collectionViewWidth - rowWidth) / 2 + offset = (collectionViewWidth - rowWidth) / 2 case .right: - offset = (collectionViewWidth - rowWidth) + offset = (collectionViewWidth - rowWidth) } for attribute in attributes { @@ -68,7 +68,6 @@ 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 } class ButtonLayoutAttributes: UICollectionViewLayoutAttributes{ @@ -88,6 +87,8 @@ 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 + var rowQuantity: Int = 0 + private var itemCache: [ButtonLayoutAttributes] = [] override func prepare() { @@ -96,101 +97,109 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { itemCache.removeAll() layoutHeight = 0.0 - guard let collectionView, let delegate else { - return - } - + guard let collectionView, let delegate else { return } // Variable to track the width of the layout at the current state when the item is being drawn var layoutWidthIterator: CGFloat = 0.0 // Only 1 section in the ButtonGroup 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 = ButtonGroupConstants.rowSpacingButton - + // Variables to track individual item width and cumultative height of all items as they are being laid out. var itemSize: CGSize = .zero - - // add top - layoutHeight += insets.top - + + // get number of buttonables let totalItems = collectionView.numberOfItems(inSection: section) + + //create rows + var rows = [ButtonCollectionViewRow]() + rows.append(ButtonCollectionViewRow()) + + let collectionViewWidth = collectionView.frame.width + for item 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 + // determine if the current buttonable will fit in the row + let rowItemCount = rows.last?.attributes.count ?? 0 + + if (layoutWidthIterator + itemSize.width) > collectionViewWidth || (rowQuantity > 0 && rowItemCount == rowQuantity) { + + // 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 + + // set the spacing of the last item of the current row to 0 + rows.last?.attributes.last?.spacing = 0 + + // add a new row + rows.append(ButtonCollectionViewRow()) } + // get the buttonable let itemButtonable = delegate.collectionView(collectionView, buttonableAtIndexPath: indexPath) + // see if there is another item in the array let nextItem = item + 1 + + // if so, get the buttonable + // and get the spacing based of the + // current buttonable and the next buttonable if nextItem < totalItems { + + //get the next buttonable let neighbor = delegate.collectionView(collectionView, buttonableAtIndexPath: IndexPath(item: nextItem, section: section)) + + // get the spacing to go between the current and next buttonable itemSpacing = ButtonGroupConstants.getHorizontalSpacing(for: itemButtonable, neighboring: neighbor) } - let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height) - //print(frame) + // create the custom layout attribute let attributes = ButtonLayoutAttributes(spacing: itemSpacing, forCellWith: indexPath) - attributes.frame = frame + attributes.frame = CGRect(x: 0, y: 0, width: itemSize.width, height: itemSize.height) attributes.isButton = isButton(buttonable: itemButtonable) - itemCache.append(attributes) - layoutWidthIterator = layoutWidthIterator + frame.width + itemSpacing + // add it to the array + rows.last?.add(attribute: attributes) + + // update the current width + // add the current frame width + the found spacing + layoutWidthIterator = layoutWidthIterator + attributes.frame.width + itemSpacing } - //add bottom - layoutHeight += itemSize.height + insets.bottom layoutWidthIterator = 0.0 + // recalculate rows x based off of positions + rows.forEach { $0.layout(for: position, with: collectionViewWidth) } - //Turn into rows and re-calculate - var rows = [ButtonCollectionViewRow]() - var currentRowY: CGFloat = -1 + // calculate the + layoutHeight = 0.0 - for attribute in itemCache { - if currentRowY != attribute.frame.midY { - currentRowY = attribute.frame.midY - rows.append(ButtonCollectionViewRow()) - } - 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 + // loop through the rows and set + // the row y position for each element + // also add to the layoutHeight for item in 0.. 0 && item < rows.count { - rowSpacing = row.hasButtons ? ButtonGroupConstants.rowSpacingButton : ButtonGroupConstants.rowSpacingTextLink - } - + if item > 0 { + rowSpacing = row.hasButtons ? ButtonGroupConstants.rowSpacingButton : ButtonGroupConstants.rowSpacingTextLink row.rowY = layoutHeight + rowSpacing layoutHeight += rowSpacing } layoutHeight += row.rowHeight } - layoutHeight += insets.bottom - + let rowAttributes = rows.flatMap { $0.attributes } itemCache = rowAttributes } @@ -227,8 +236,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { guard let collectionView = collectionView else { return 0 } - let insets = collectionView.contentInset - return collectionView.bounds.width - (insets.left + insets.right) + return collectionView.bounds.width } }