From ae6c38062755ee4a3f10de7da4eb2a3349a6f785 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 8 Dec 2022 19:34:04 -0600 Subject: [PATCH 01/10] removed old code Signed-off-by: Matt Bruce --- VDS/Classes/ColorConfiguration.swift | 206 ------------------ VDS/Components/Buttons/Button/Button.swift | 16 -- .../Buttons/TextLinkCaret/TextLinkCaret.swift | 8 +- VDS/Components/Checkbox/Checkbox.swift | 2 +- VDS/Components/RadioBox/RadioBox.swift | 2 +- VDS/Components/RadioButton/RadioButton.swift | 2 +- VDS/Components/RadioSwatch/RadioSwatch.swift | 2 +- .../TextFields/EntryField/EntryField.swift | 23 -- .../TextEntryField/TextEntryField.swift | 26 --- VDS/Components/Toggle/Toggle.swift | 4 +- 10 files changed, 9 insertions(+), 282 deletions(-) diff --git a/VDS/Classes/ColorConfiguration.swift b/VDS/Classes/ColorConfiguration.swift index c14e6f1c..a1288aa2 100644 --- a/VDS/Classes/ColorConfiguration.swift +++ b/VDS/Classes/ColorConfiguration.swift @@ -112,209 +112,3 @@ open class SurfaceColorConfiguration: ObjectColorable { return getColor(object.surface) } } - -///------------------------------------------------------------------- -///MARK -- DisabledSurfaceColorable -///------------------------------------------------------------------- -public protocol DisabledSurfaceColorable: ObjectColorable { - var disabled: SurfaceColorConfiguration { get set } - var enabled: SurfaceColorConfiguration { get set } -} - -extension DisabledSurfaceColorable { - public func getDisabledColor (_ object: M) -> UIColor { - object.disabled ? disabled.getColor(object) : enabled.getColor(object) - } -} - -/// Meant to be used in a Object that implements the following interfaces for 4 possible color combinations -/// - Disabling (var disabled: Bool) -/// - Surfaceable (var surface: Surface) -/// -/// let model = TestModel() -/// model.surface = .dark -/// model.disabled = false -/// -/// let config = DisabledSurfaceColorConfiguration() -/// -/// //disabled == false -/// config.enabled.lightColor = .black -/// config.enabled.darkColor = .white -/// -/// //disabled == true -/// config.disabled.lightColor = .gray -/// config.disabled.darkColor = .lightGray -/// -/// let textColor = config.getColor(model) //returns .white -/// -/// -open class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable { - public typealias ObjectType = Surfaceable & Disabling - public var disabled = SurfaceColorConfiguration() - public var enabled = SurfaceColorConfiguration() - - required public init(){} - - public func getColor(_ object: any ObjectType) -> UIColor { - getDisabledColor(object) - } -} - -///------------------------------------------------------------------- -///MARK -- BinaryColorable -///------------------------------------------------------------------- -public protocol BinaryColorable{ - var useTrueColor: Bool { get } -} - -extension BinaryColorable where Self: Control { - public var useTrueColor: Bool { return isSelected } -} - -///------------------------------------------------------------------- -///MARK -- BinarySurfaceColorable -///------------------------------------------------------------------- -public protocol BinarySurfaceColorable: ObjectColorable { - var forTrue: SurfaceColorConfiguration { get set } - var forFalse: SurfaceColorConfiguration { get set } -} - -extension BinarySurfaceColorable { - public func getBinaryColor(_ object: M) -> UIColor { - object.useTrueColor ? forTrue.getColor(object) : forFalse.getColor(object) - } -} - -/// Meant to be used in a Object that implements the following interfaces for 4 possible color combinations -/// - BinaryColorable (var userTrueColor: Bool) -/// - Surfaceable (var surface: Surface) -/// -/// let model = TestModel() -/// model.surface = .dark -/// model.on = true //this is read in the extension var userTrueColor -/// let config = BinarySurfaceColorConfiguration() -/// -/// //True from BinaryColorable.userTrueColor -/// config.forTrue.lightColor = .black -/// config.forTrue.darkColor = .white -/// -/// //False from BinaryColorable.userTrueColor -/// config.forFalse.lightColor = .red -/// config.forFalse.darkColor = .red -/// -/// let textColor = config.getColor(model) //returns .white -/// -/// -final public class BinarySurfaceColorConfiguration: BinarySurfaceColorable { - public typealias ObjectType = Surfaceable & BinaryColorable - public var forTrue = SurfaceColorConfiguration() - public var forFalse = SurfaceColorConfiguration() - - required public init(){} - - public func getColor(_ object: any ObjectType) -> UIColor { - getBinaryColor(object) - } -} - -///------------------------------------------------------------------- -///MARK -- BinaryDisabledSurfaceColorable -///------------------------------------------------------------------- - -public protocol BinaryDisabledSurfaceColorable: ObjectColorable { - var forTrue: DisabledSurfaceColorConfiguration { get set } - var forFalse: DisabledSurfaceColorConfiguration { get set } -} - -extension BinaryDisabledSurfaceColorable { - public func getBinaryColor(_ object: M) -> UIColor { - object.useTrueColor ? forTrue.getColor(object) : forFalse.getColor(object) - } -} - -/// Meant to be used in a Object that implements the following interfaces for 8 possible color combinations -/// - BinaryColorable (var userTrueColor: Bool) -/// - Disabling (var disabled: Bool) -/// - Surfaceable (var surface: Surface) -/// -/// let model = TestModel() -/// model.on = false -/// model.disabled = false -/// model.surface = .light -/// let config = BinaryDisabledSurfaceColorConfiguration() -/// -/// //True -/// config.forTrue.enabled.lightColor = .black -/// config.forTrue.enabled.darkColor = .white -/// config.forTrue.disabled.lightColor = .darkGray -/// config.forTrue.disabled.darkColor = .lightGray -/// -/// //False -/// config.forFalse.enabled.lightColor = .red -/// config.forFalse.enabled.darkColor = .red -/// config.forFalse.disabled.lightColor =.darkGray -/// config.forFalse.disabled.darkColor = .lightGray -/// -/// let textColor = config.getColor(model) -/// -/// -final public class BinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfaceColorable { - public typealias ObjectType = Disabling & Surfaceable & BinaryColorable - public var forTrue = DisabledSurfaceColorConfiguration() - public var forFalse = DisabledSurfaceColorConfiguration() - - required public init(){} - - public func getColor(_ object: any ObjectType) -> UIColor { - getBinaryColor(object) - } -} - -public class ControlStateColorConfiguration: ObjectColorable { - public typealias ObjectType = UIControl & Surfaceable - public var disabled = SurfaceColorConfiguration() - public var normal = SurfaceColorConfiguration() - public var highlighted: SurfaceColorConfiguration? - public var selected: SurfaceColorConfiguration? - public var error: SurfaceColorConfiguration? - - required public init(){} - - public func setColorable(_ config: SurfaceColorConfiguration, for state: UIControl.State) { - switch state { - case .disabled: - disabled = config - - case .highlighted: - highlighted = config - - case .selected: - selected = config - - case .error: - error = config - - default: - normal = config - } - } - - public func getColor(_ object: any ObjectType) -> UIColor { - - if object.state == .disabled { - return disabled.getColor(object) - - } else if let highlighted, object.state == .highlighted { - return highlighted.getColor(object) - - } else if let selected, object.state == .selected { - return selected.getColor(object) - - } else if let error, object.state == .error { - return error.getColor(object) - - } else { - return normal.getColor(object) - } - } -} diff --git a/VDS/Components/Buttons/Button/Button.swift b/VDS/Components/Buttons/Button/Button.swift index ed615958..e68c818d 100644 --- a/VDS/Components/Buttons/Button/Button.swift +++ b/VDS/Components/Buttons/Button/Button.swift @@ -170,22 +170,6 @@ open class Button: ButtonBase, Useable { minWidthConstraint?.isActive = true } } - - //-------------------------------------------------- - // MARK: - PRIVATE - //-------------------------------------------------- - - private class UseableColorConfiguration: ObjectColorable { - typealias ObjectType = Buttonable & Useable - public var primary = ControlStateColorConfiguration() - public var secondary = ControlStateColorConfiguration() - - required public init(){} - - public func getColor(_ object: ObjectType) -> UIColor { - return object.use == .primary ? primary.getColor(object) : secondary.getColor(object) - } - } } fileprivate extension ButtonSize { diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index 0013c77a..a6ac59c7 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -159,11 +159,9 @@ internal class CaretView: View { public var size: CaretSize? { didSet{ didChange() } } - public var colorConfiguration: AnyColorable = DisabledSurfaceColorConfiguration().with { - $0.disabled.lightColor = VDSColor.elementsSecondaryOnlight - $0.disabled.darkColor = VDSColor.elementsSecondaryOndark - $0.enabled.lightColor = VDSColor.elementsPrimaryOnlight - $0.enabled.darkColor = VDSColor.elementsPrimaryOndark + public var colorConfiguration: AnyColorable = ViewColorConfiguration().with { + $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: true) + $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) }.eraseToAnyColorable() diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index 97bd6361..91c08aab 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -27,7 +27,7 @@ public class SoloCheckbox: CheckboxBase{ } @objc(VDSCheckboxBase) -open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Errorable { +open class CheckboxBase: Control, Accessable, DataTrackable, Errorable { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index ad3846db..7951c4cd 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -27,7 +27,7 @@ public class SoloRadioBox: RadioBoxBase{ } @objc(VDSRadioBoxBase) -open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{ +open class RadioBoxBase: Control, Accessable, DataTrackable{ //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index fd36bded..674a0a6f 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -34,7 +34,7 @@ public class SoloRadioButton: RadioButtonBase { } @objc(VDSRadioButtonBase) -open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, Errorable { +open class RadioButtonBase: Control, Accessable, DataTrackable, Errorable { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- diff --git a/VDS/Components/RadioSwatch/RadioSwatch.swift b/VDS/Components/RadioSwatch/RadioSwatch.swift index 0ee397a7..5c36c575 100644 --- a/VDS/Components/RadioSwatch/RadioSwatch.swift +++ b/VDS/Components/RadioSwatch/RadioSwatch.swift @@ -31,7 +31,7 @@ public class SolorRadioSwatch: RadioSwatchBase{ } @objc(VDSRadioSwatchBase) -open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable { +open class RadioSwatchBase: Control, Accessable, DataTrackable { //-------------------------------------------------- // MARK: - Initializers diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index 59dda2b1..d38d4453 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -358,26 +358,3 @@ open class EntryField: Control, Accessable { } } } - -//-------------------------------------------------- -// MARK: - Color Class Configurations -//-------------------------------------------------- -internal class ErrorDisabledSurfaceColorConfiguration: DisabledSurfaceColorable { - typealias ModelType = Errorable & Surfaceable & Disabling - var error = SurfaceColorConfiguration() - var disabled = SurfaceColorConfiguration() - var enabled = SurfaceColorConfiguration() - - required public init(){} - - func getColor(_ viewModel: any ModelType) -> UIColor { - //only show error is enabled and showError == true - let showErrorColor = !viewModel.disabled && viewModel.showError - - if showErrorColor { - return error.getColor(viewModel) - } else { - return getDisabledColor(viewModel) - } - } -} diff --git a/VDS/Components/TextFields/TextEntryField/TextEntryField.swift b/VDS/Components/TextFields/TextEntryField/TextEntryField.swift index 92cf1cc6..280a66e0 100644 --- a/VDS/Components/TextFields/TextEntryField/TextEntryField.swift +++ b/VDS/Components/TextFields/TextEntryField/TextEntryField.swift @@ -185,32 +185,6 @@ open class TextEntryFieldBase: EntryField { } } } - - internal class TextEntryFieldColorConfiguration: DisabledSurfaceColorable { - var success = SurfaceColorConfiguration() - var error = SurfaceColorConfiguration() - var disabled = SurfaceColorConfiguration() - var enabled = SurfaceColorConfiguration() - - required init(){} - - func getColor(_ object: TextEntryField) -> UIColor { - //only show error is enabled and showError == true - let showErrorColor = !object.disabled && object.showError - let showSuccessColor = !object.disabled && object.showSuccess - - if showErrorColor { - return error.getColor(object) - - } else if showSuccessColor { - return success.getColor(object) - - } else { - return getDisabledColor(object) - } - } - } - } extension TextEntryFieldType { diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 775768d3..d5b86437 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -42,7 +42,7 @@ public class Toggle: ToggleBase{ } @objc(VDSToggleBase) -open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { +open class ToggleBase: Control, Accessable, DataTrackable { //-------------------------------------------------- // MARK: - Initializers @@ -97,7 +97,7 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { private var knobColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forState: .normal) - $0.setSurfaceColors(VDSColor.paletteGray95, VDSColor.paletteGray44, forState: .disabled) + $0.setSurfaceColors(VDSColor.paletteGray95, VDSColor.paletteGray44, forState: .disabled) $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forState: .selected) } From 62befe1a32e1d2dfbddea3fef68121d94b132293 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 13 Dec 2022 11:13:44 -0500 Subject: [PATCH 02/10] added percentage to button group Signed-off-by: Matt Bruce --- VDS/Components/Buttons/Button/Button.swift | 2 +- .../Buttons/ButtonGroup/ButtonGroup.swift | 20 +++++++ .../ButtonGroupPositionLayout.swift | 56 ++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Buttons/Button/Button.swift b/VDS/Components/Buttons/Button/Button.swift index e68c818d..ee2c0ab5 100644 --- a/VDS/Components/Buttons/Button/Button.swift +++ b/VDS/Components/Buttons/Button/Button.swift @@ -172,7 +172,7 @@ open class Button: ButtonBase, Useable { } } -fileprivate extension ButtonSize { +internal extension ButtonSize { var height: CGFloat { switch self { diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 916609e4..5e045228 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -32,6 +32,9 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega //If provided, width of Button components will be rendered based on this value. If omitted, default button widths are rendered. open var buttonWidth: CGFloat? { didSet { + if let buttonWidth, let buttonPercentage, buttonWidth > 0, buttonPercentage > 0{ + self.buttonPercentage = nil + } buttons.forEach { button in if let button = button as? Button { button.width = buttonWidth @@ -40,6 +43,23 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega didChange() } } + + var _buttonPercentage: CGFloat? + open var buttonPercentage: CGFloat? { + get { _buttonPercentage } + set { + if let newValue, newValue <= 100.0, rowQuantity > 0 { + _buttonPercentage = newValue + } else { + _buttonPercentage = nil + } + if let buttonWidth, let buttonPercentage, buttonWidth > 0, buttonPercentage > 0 { + self.buttonWidth = nil + } + positionLayout.buttonPercentage = buttonPercentage + didChange() + } + } //-------------------------------------------------- // MARK: - Private Properties diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index 5ab5fe37..d7f34f4e 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -21,6 +21,8 @@ class ButtonCollectionViewRow { var hasButtons: Bool { attributes.contains(where: { $0.isButton }) } + + var buttonPercentage: CGFloat? var rowWidth: CGFloat { return attributes.reduce(0, { result, attribute -> CGFloat in @@ -50,6 +52,54 @@ class ButtonCollectionViewRow { let height = rowHeight attributes.last?.spacing = 0 + //check to see if you have buttons and there is a percentage + if let buttonPercentage, hasButtons, buttonPercentage > 0 { + + var usedSpace = 0.0 + //get the width for the buttons + for attribute in attributes { + if !attribute.isButton { + usedSpace += attribute.frame.width + } + usedSpace += attribute.spacing + } + let buttonAvailableSpace = collectionViewWidth - usedSpace + let realPercentage = (buttonPercentage / 100) + let buttonWidth = realPercentage * buttonAvailableSpace +// print("buttonPercentage :\(realPercentage)") +// print("collectionView width:\(collectionViewWidth)") +// print("usedSpace width:\(usedSpace)") +// print("button available width:\(buttonAvailableSpace)") + print("each button width:\(buttonWidth)\n") + print("minimum widht:\(ButtonSize.large.minimumWidth)") + // test sizing + var testSize = 0.0 + var buttonCount = 0.0 + for attribute in attributes { + if attribute.isButton { + testSize += buttonWidth + buttonCount += 1 + } + } + + if buttonWidth >= ButtonSize.large.minimumWidth { + if testSize <= buttonAvailableSpace { + for attribute in attributes { + if attribute.isButton { + attribute.frame.size.width = buttonWidth + } + } + } else { + let distributedSize = buttonAvailableSpace / buttonCount + for attribute in attributes { + if attribute.isButton { + attribute.frame.size.width = distributedSize + } + } + } + } + } + switch position { case .left: break @@ -96,6 +146,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { var layoutHeight: CGFloat = 0.0 var position: ButtonPosition = .left var rowQuantity: Int = 0 + var buttonPercentage: CGFloat? private var itemCache: [ButtonLayoutAttributes] = [] @@ -205,7 +256,10 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { } // recalculate rows x based off of positions - rows.forEach { $0.layout(for: position, with: collectionViewWidth) } + rows.forEach { + $0.buttonPercentage = buttonPercentage + $0.layout(for: position, with: collectionViewWidth) + } let rowAttributes = rows.flatMap { $0.attributes } itemCache = rowAttributes From db9c86b43fc3948fb0d5364765c9a6ad4bdbcd9c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 13 Dec 2022 12:12:15 -0500 Subject: [PATCH 03/10] removed comments Signed-off-by: Matt Bruce --- .../Buttons/ButtonGroup/ButtonGroupPositionLayout.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index d7f34f4e..a9307adf 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -70,8 +70,8 @@ class ButtonCollectionViewRow { // print("collectionView width:\(collectionViewWidth)") // print("usedSpace width:\(usedSpace)") // print("button available width:\(buttonAvailableSpace)") - print("each button width:\(buttonWidth)\n") - print("minimum widht:\(ButtonSize.large.minimumWidth)") +// print("each button width:\(buttonWidth)\n") +// print("minimum widht:\(ButtonSize.large.minimumWidth)") // test sizing var testSize = 0.0 var buttonCount = 0.0 From 627dc99c426537dd9863d03f52bd064ed8d341a2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 11:46:25 -0600 Subject: [PATCH 04/10] allowed color param Signed-off-by: Matt Bruce --- VDS/Extensions/UIView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Extensions/UIView.swift b/VDS/Extensions/UIView.swift index eb041400..6db07dd2 100644 --- a/VDS/Extensions/UIView.swift +++ b/VDS/Extensions/UIView.swift @@ -119,9 +119,9 @@ extension UIView { extension UIView { - public func debugBorder(show shouldShow: Bool = true) { + public func debugBorder(show shouldShow: Bool = true, color: UIColor = .red) { if shouldShow { - layer.borderColor = UIColor.red.cgColor + layer.borderColor = color.cgColor layer.borderWidth = 1 } else { layer.borderColor = UIColor.clear.cgColor From 1e44d9738dc672e6af61a33204dea9a33dcb34c0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 11:46:35 -0600 Subject: [PATCH 05/10] added hitArea Signed-off-by: Matt Bruce --- .../Buttons/Button/ButtonBase.swift | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Buttons/Button/ButtonBase.swift b/VDS/Components/Buttons/Button/ButtonBase.swift index 005c542a..cb69771f 100644 --- a/VDS/Components/Buttons/Button/ButtonBase.swift +++ b/VDS/Components/Buttons/Button/ButtonBase.swift @@ -19,7 +19,11 @@ public protocol Buttonable: UIControl, Surfaceable, Disabling { @objc(VDSButtonBase) open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable { - + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + private let hitAreaHeight = 44.0 + //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- @@ -194,4 +198,28 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab setAttributedTitle(mutableText, for: .normal) setAttributedTitle(mutableText, for: .highlighted) } + + open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let size = intrinsicContentSize + // Create a minimumHitArea variable with a value that represents the minimum size of the hit area you want to create for the button. + let minimumHitArea = CGSize(width: size.width, height: hitAreaHeight) + + // Create a new hitFrame variable that is the same size as the minimumHitArea variable, but is centered on the button's frame. + let hitFrame = CGRect( + x: self.bounds.midX - minimumHitArea.width / 2, + y: self.bounds.midY - minimumHitArea.height / 2, + width: minimumHitArea.width, + height: minimumHitArea.height + ) + + // If the point that was passed to the hitTest(_:with:) method is within the hitFrame, return the button itself. This will cause the button to handle the touch event. + if hitFrame.contains(point) { + return self + } + + // If the point is not within the hitFrame, return nil. This will cause the touch event to be handled by another view. + return nil + } + } + From 81f73411489173ec726dc1f5eeca02bc8a84da09 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 11:47:08 -0600 Subject: [PATCH 06/10] refactored to remove height now the a hit area exists Signed-off-by: Matt Bruce --- .../Buttons/TextLink/TextLink.swift | 9 +-------- .../Buttons/TextLinkCaret/TextLinkCaret.swift | 20 ++++++++----------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/VDS/Components/Buttons/TextLink/TextLink.swift b/VDS/Components/Buttons/TextLink/TextLink.swift index 4f543ac5..ec03610c 100644 --- a/VDS/Components/Buttons/TextLink/TextLink.swift +++ b/VDS/Components/Buttons/TextLink/TextLink.swift @@ -17,7 +17,6 @@ open class TextLink: ButtonBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - private var heightConstraint: NSLayoutConstraint? private var lineHeightConstraint: NSLayoutConstraint? //-------------------------------------------------- @@ -83,9 +82,6 @@ open class TextLink: ButtonBase { lineHeightConstraint = line.heightAnchor.constraint(equalToConstant: 1.0) lineHeightConstraint?.isActive = true } - - heightConstraint = heightAnchor.constraint(equalToConstant: height) - heightConstraint?.isActive = true } open override func reset() { @@ -99,18 +95,15 @@ open class TextLink: ButtonBase { // MARK: - Overrides //-------------------------------------------------- open override var intrinsicContentSize: CGSize { - let size = titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize - return CGSize(width: size.width, height: height) + return titleLabel?.intrinsicContentSize ?? super.intrinsicContentSize } open override func updateView() { //need to set the properties so the super class //can render out the label correctly - heightConstraint?.constant = height line.backgroundColor = textColor //always call last so the label is rendered super.updateView() } - } diff --git a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift index a6ac59c7..f6fa4a20 100644 --- a/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift +++ b/VDS/Components/Buttons/TextLinkCaret/TextLinkCaret.swift @@ -21,12 +21,10 @@ open class TextLinkCaret: ButtonBase { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - private var heightConstraint: NSLayoutConstraint? - open override var typograpicalStyle: TypographicalStyle { TypographicalStyle.BoldBodyLarge } - + private var caretView = CaretView().with { $0.size = CaretView.CaretSize.small(.vertical) $0.lineWidth = 2 @@ -64,7 +62,7 @@ open class TextLinkCaret: ButtonBase { open override var textColor: UIColor { textColorConfiguration.getColor(self) } - + private var textColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forState: .disabled) @@ -91,9 +89,7 @@ open class TextLinkCaret: ButtonBase { //-------------------------------------------------- open override func setup() { super.setup() - //constraints - heightAnchor.constraint(greaterThanOrEqualToConstant: height).isActive = true - + let size = caretView.size!.dimensions() caretView.frame = .init(x: 0, y: 0, width: size.width, height: size.height) } @@ -115,9 +111,9 @@ open class TextLinkCaret: ButtonBase { if let caretWidth = caretView.size?.dimensions().width { itemWidth += caretWidth } - return CGSize(width: itemWidth, height: height) + return CGSize(width: itemWidth, height: size.height) } - + open override func updateView() { let updatedText = text ?? "" @@ -129,9 +125,9 @@ open class TextLinkCaret: ButtonBase { let location = iconPosition == .right ? updatedText.count : 0 imageAttribute = ImageLabelAttribute(location: location, - image: image, - tintColor: textColor) - + image: image, + tintColor: textColor) + super.updateView() } From fd8e68f1c3e465486e39ed3bfd583f98f45714a9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 11:48:14 -0600 Subject: [PATCH 07/10] refactored spacing algos since we are now not resizing rectangle heights based off of the tallest container in the row Signed-off-by: Matt Bruce --- .../ButtonGroup/ButtonGroupConstants.swift | 155 +++++++++--------- .../ButtonGroupPositionLayout.swift | 34 ++-- 2 files changed, 96 insertions(+), 93 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift index 015be8b4..ff8605d0 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupConstants.swift @@ -8,77 +8,28 @@ 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 let defaultSpace = 12.0 + + enum ButtonSpacingAxis { + case horizontal, vertical } - static func getVerticalSpacing(for primary: Buttonable, neighboring: Buttonable) -> CGFloat { - let defaultSpace = 12.0 + /// This will determine the spacing that will go between 2 buttonables either horizontally or vertically + /// - Parameters: + /// - axis: horizontal/vertical + /// - primary: first buttonable + /// - neighboring: next buttonable based off of axis + /// - Returns: float value + static func getSpacing(for axis: ButtonSpacingAxis, with primary: Buttonable, neighboring: Buttonable) -> CGFloat { + //large button if let button = primary as? Button, button.size == .large { if let neighboringButton = neighboring as? Button, neighboringButton.size == .large { - return 12.0 + return axis == .horizontal ? 12.0 : 12.0 + } else if neighboring is TextLinkCaret { + return axis == .horizontal ? 24.0 : 24.0 } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { - return 16.0 - } else if let _ = neighboring as? TextLinkCaret { - return 24.0 + return axis == .horizontal ? 16.0 : 16.0 } else { return defaultSpace } @@ -86,11 +37,11 @@ struct ButtonGroupConstants { //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 + return axis == .horizontal ? 16.0 : 16.0 + } else if neighboring is TextLinkCaret { + return axis == .horizontal ? 24.0 : 24.0 } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { - return 24.0 + return axis == .horizontal ? 16.0 : 24.0 } else { return defaultSpace } @@ -98,11 +49,11 @@ struct ButtonGroupConstants { //text link caret else if let _ = primary as? TextLinkCaret { if let neighboringButton = neighboring as? Button, neighboringButton.size == .large { - return 16.0 + return axis == .horizontal ? 24.0 : 24.0 } else if let _ = neighboring as? TextLinkCaret { - return 24.0 + return axis == .horizontal ? 24.0 : 24.0 } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large { - return 24.0 + return axis == .horizontal ? 24.0 : 24.0 } else { return defaultSpace } @@ -110,9 +61,9 @@ struct ButtonGroupConstants { //small button else if let button = primary as? Button, button.size == .small { if let neighboringButton = neighboring as? Button, neighboringButton.size == .small { - return 12.0 + return axis == .horizontal ? 12.0 : 12.0 } else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small { - return 24.0 + return axis == .horizontal ? 16.0 : 24.0 } else { return defaultSpace } @@ -120,9 +71,9 @@ struct ButtonGroupConstants { //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 if let neighboringButton = neighboring as? Button, neighboringButton.size == .small { - return 24.0 + return axis == .horizontal ? 16.0 : 24.0 + } else if let _ = neighboring as? Button { + return axis == .horizontal ? 16.0 : 32.0 } else { return defaultSpace } @@ -133,4 +84,54 @@ struct ButtonGroupConstants { } } + + // + + /// Gets the tallest buttonables within the row + /// - Parameter row: Row that includes the attributes + /// - Returns: Array of [ButtonLayoutAttributes] of the tallest items + private static func getTallestAttributes(for row: ButtonCollectionViewRow) -> [ButtonLayoutAttributes] { + var height = 0.0 + var foundIndexes:[Int] = [] + for (index, attribute) in row.attributes.enumerated() { + if attribute.frame.height >= height { + height = attribute.frame.height + foundIndexes.append(index) + } + } + return foundIndexes.compactMap { row.attributes[$0] } + } + + + /// Gets the vertical spacing that will go between rows. + /// - Parameters: + /// - row: Primary row that the space will go between + /// - neighboringRow: Secondary row that will be below the Primary + /// - Returns: Amount of space that should live between these rows based off of the items. The largest space will win when the comparison occurs. + static func getVerticalSpacing(for row: ButtonCollectionViewRow, neighboringRow: ButtonCollectionViewRow?) -> CGFloat { + // if the neighboringRow is nil, this is the last row in the collection + // so return no space + guard let neighboringRow else { return 0.0 } + + let primaryTallestAttributes = getTallestAttributes(for: row) + let neighboringTallestAttributes = getTallestAttributes(for: neighboringRow) + + if primaryTallestAttributes.count > 0 && neighboringTallestAttributes.count > 0 { + // If there is a tie for “tallest child,” the tiebreaker criteria is to refer to the child with the larger space requirement (for vertical spacing). + var largestVerticalSpace = defaultSpace + + primaryTallestAttributes.forEach { primaryAttribute in + neighboringTallestAttributes.forEach { neighboringTallestAttribute in + let space = getSpacing(for: .vertical, with: primaryAttribute.buttonable!, neighboring: neighboringTallestAttribute.buttonable!) + if space > largestVerticalSpace { + largestVerticalSpace = space + } + } + } + return largestVerticalSpace + } + else { + return defaultSpace + } + } } diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift index a9307adf..eead60ba 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupPositionLayout.swift @@ -17,7 +17,7 @@ class ButtonCollectionViewRow { func add(attribute: ButtonLayoutAttributes) { attributes.append(attribute) } - + var hasButtons: Bool { attributes.contains(where: { $0.isButton }) } @@ -33,7 +33,7 @@ class ButtonCollectionViewRow { var rowHeight: CGFloat { attributes.compactMap{$0.frame.height}.max() ?? 0 } - + var maxHeightIndexPath: IndexPath { let maxHeight = rowHeight return attributes.first(where: {$0.frame.height == maxHeight})!.indexPath @@ -112,7 +112,8 @@ class ButtonCollectionViewRow { for attribute in attributes { attribute.frame.origin.x = offset if attribute.frame.height < height { - attribute.frame.size.height = height + //recalibrate the y to vertically center align rect + attribute.frame.origin.y += (height - attribute.frame.size.height) / 2 } offset += attribute.frame.width + attribute.spacing } @@ -130,11 +131,20 @@ protocol ButtongGroupPositionLayoutDelegate: AnyObject { class ButtonLayoutAttributes: UICollectionViewLayoutAttributes{ var spacing: CGFloat = 0 - var isButton: Bool = false + + var buttonable: Buttonable? + + var isButton: Bool { + guard buttonable is Button else { return false } + return true + } + convenience init(spacing: CGFloat, + buttonable: Buttonable, forCellWith indexPath: IndexPath) { self.init(forCellWith: indexPath) self.spacing = spacing + self.buttonable = buttonable } } @@ -218,13 +228,12 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { 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) + itemSpacing = ButtonGroupConstants.getSpacing(for: .horizontal, with: itemButtonable, neighboring: neighbor) } // create the custom layout attribute - let attributes = ButtonLayoutAttributes(spacing: itemSpacing, forCellWith: indexPath) + let attributes = ButtonLayoutAttributes(spacing: itemSpacing, buttonable: itemButtonable, forCellWith: indexPath) attributes.frame = CGRect(x: 0, y: 0, width: itemSize.width, height: itemSize.height) - attributes.isButton = isButton(buttonable: itemButtonable) // add it to the array rows.last?.add(attribute: attributes) @@ -247,7 +256,8 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { var rowSpacing = 0.0 if item > 0 { - rowSpacing = 12.0 + let prevRow = rows[item - 1] + rowSpacing = ButtonGroupConstants.getVerticalSpacing(for: prevRow, neighboringRow: row) row.rowY = layoutHeight + rowSpacing layoutHeight += rowSpacing } @@ -265,14 +275,6 @@ class ButtonGroupPositionLayout: UICollectionViewLayout { itemCache = rowAttributes } - - func isButton(buttonable: Buttonable) -> Bool{ - if let _ = buttonable as? Button { - return true - } else { - return false - } - } override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? { var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = [] From 04bbb8948a61125714adb7ba46bf7e76dbb7fa7b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 11:48:26 -0600 Subject: [PATCH 08/10] created new cell for button group Signed-off-by: Matt Bruce --- .../Buttons/ButtonGroup/ButtonGroup.swift | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 5e045228..c5645211 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -84,7 +84,7 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega $0.translatesAutoresizingMaskIntoConstraints = false $0.dataSource = self $0.delegate = self - $0.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") + $0.register(ButtonCollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") } }() @@ -166,11 +166,13 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let button = buttons[indexPath.row] - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? ButtonCollectionViewCell else { return UICollectionViewCell() } cell.subviews.forEach { $0.removeFromSuperview() } cell.addSubview(button) + cell.buttonable = button button.pinLeading() button.pinTrailing() + cell.buttonIntrinsicContentSize = button.intrinsicContentSize button.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true return cell } @@ -193,3 +195,31 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega } + +public class ButtonCollectionViewCell: UICollectionViewCell { + + var buttonIntrinsicContentSize: CGSize = .zero + var buttonable: Buttonable? + + open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + // Create a minimumHitArea variable with a value that represents the minimum size of the hit area you want to create for the button. + let minimumHitArea = CGSize(width: buttonIntrinsicContentSize.width, height: 44) + + // Create a new hitFrame variable that is the same size as the minimumHitArea variable, but is centered on the button's frame. + let hitFrame = CGRect( + x: self.bounds.midX - minimumHitArea.width / 2, + y: self.bounds.midY - minimumHitArea.height / 2, + width: minimumHitArea.width, + height: minimumHitArea.height + ) + + // If the point that was passed to the hitTest(_:with:) method is within the hitFrame, return the button itself. This will cause the button to handle the touch event. + if hitFrame.contains(point) { + return buttonable + } + + // If the point is not within the hitFrame, return nil. This will cause the touch event to be handled by another view. + return nil + } + +} From 464dd5b5e9ff304d7bbfbd3d0e159f1719c84a9d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 12:04:51 -0600 Subject: [PATCH 09/10] create separate file for cell Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 6 ++- .../ButtonGroupCollectionViewCell.swift | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 VDS/Components/Buttons/ButtonGroup/ButtonGroupCollectionViewCell.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index c89651a4..5e4a1b76 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF4292D371F00998C17 /* ButtonBase.swift */; }; EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */; }; EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FF0029424ACB00998C17 /* UIControl.swift */; }; + EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.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 */; }; @@ -159,6 +160,7 @@ EAB5FEF4292D371F00998C17 /* ButtonBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBase.swift; sourceTree = ""; }; EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = ""; }; EAB5FF0029424ACB00998C17 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; + EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.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 = ""; }; @@ -229,8 +231,9 @@ isa = PBXGroup; children = ( EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */, - EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */, + EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */, EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */, + EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */, ); path = ButtonGroup; sourceTree = ""; @@ -668,6 +671,7 @@ EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */, EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */, + EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */, EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */, EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */, EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */, diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroupCollectionViewCell.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroupCollectionViewCell.swift new file mode 100644 index 00000000..13e56a8e --- /dev/null +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroupCollectionViewCell.swift @@ -0,0 +1,40 @@ +// +// ButtonGroupCollectionViewCell.swift +// VDS +// +// Created by Matt Bruce on 12/15/22. +// + +import Foundation +import UIKit + + +/// Cell is needed since the buttonable hitArea "can" be outside of it's container rectangle +public class ButtonGroupCollectionViewCell: UICollectionViewCell { + + var buttonable: Buttonable? + + open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let buttonable else { return nil } + + // Create a minimumHitArea variable with a value that represents the minimum size of the hit area you want to create for the button. + let minimumHitArea = CGSize(width: buttonable.intrinsicContentSize.width, height: 44) + + // Create a new hitFrame variable that is the same size as the minimumHitArea variable, but is centered on the button's frame. + let hitFrame = CGRect( + x: self.bounds.midX - minimumHitArea.width / 2, + y: self.bounds.midY - minimumHitArea.height / 2, + width: minimumHitArea.width, + height: minimumHitArea.height + ) + + // If the point that was passed to the hitTest(_:with:) method is within the hitFrame, return the button itself. This will cause the button to handle the touch event. + if hitFrame.contains(point) { + return buttonable + } + + // If the point is not within the hitFrame, return nil. This will cause the touch event to be handled by another view. + return nil + } + +} From bccd90e8af12a9837653918e6d14933e6decc7d2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Dec 2022 12:05:06 -0600 Subject: [PATCH 10/10] updated to use protocol for buttonable Signed-off-by: Matt Bruce --- .../Buttons/ButtonGroup/ButtonGroup.swift | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index c5645211..1362d68d 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -84,7 +84,7 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega $0.translatesAutoresizingMaskIntoConstraints = false $0.dataSource = self $0.delegate = self - $0.register(ButtonCollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") + $0.register(ButtonGroupCollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") } }() @@ -166,13 +166,12 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let button = buttons[indexPath.row] - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? ButtonCollectionViewCell else { return UICollectionViewCell() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? ButtonGroupCollectionViewCell else { return UICollectionViewCell() } cell.subviews.forEach { $0.removeFromSuperview() } cell.addSubview(button) cell.buttonable = button button.pinLeading() button.pinTrailing() - cell.buttonIntrinsicContentSize = button.intrinsicContentSize button.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true return cell } @@ -195,31 +194,3 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega } - -public class ButtonCollectionViewCell: UICollectionViewCell { - - var buttonIntrinsicContentSize: CGSize = .zero - var buttonable: Buttonable? - - open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - // Create a minimumHitArea variable with a value that represents the minimum size of the hit area you want to create for the button. - let minimumHitArea = CGSize(width: buttonIntrinsicContentSize.width, height: 44) - - // Create a new hitFrame variable that is the same size as the minimumHitArea variable, but is centered on the button's frame. - let hitFrame = CGRect( - x: self.bounds.midX - minimumHitArea.width / 2, - y: self.bounds.midY - minimumHitArea.height / 2, - width: minimumHitArea.width, - height: minimumHitArea.height - ) - - // If the point that was passed to the hitTest(_:with:) method is within the hitFrame, return the button itself. This will cause the button to handle the touch event. - if hitFrame.contains(point) { - return buttonable - } - - // If the point is not within the hitFrame, return nil. This will cause the touch event to be handled by another view. - return nil - } - -}