Merge branch 'feature/buttonGroupUpdate' into 'develop'

removed old code

See merge request BPHV_MIPS/vds_ios!21
This commit is contained in:
Bruce, Matt R 2022-12-15 21:07:23 +00:00
commit db64bfe2fd
18 changed files with 269 additions and 403 deletions

View File

@ -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 = "<group>"; };
EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = "<group>"; };
EAB5FF0029424ACB00998C17 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.swift; sourceTree = "<group>"; };
EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = "<group>"; };
EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = "<group>"; };
EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
@ -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 */,

View File

@ -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<M: Surfaceable & Disabling> (_ 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<M: Surfaceable & BinaryColorable>(_ 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<M: Disabling & Surfaceable & BinaryColorable>(_ 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)
}
}
}

View File

@ -170,25 +170,9 @@ 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 {
internal extension ButtonSize {
var height: CGFloat {
switch self {

View File

@ -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
}
}

View File

@ -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
@ -64,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(ButtonGroupCollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell")
}
}()
@ -146,9 +166,10 @@ 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? ButtonGroupCollectionViewCell else { return UICollectionViewCell() }
cell.subviews.forEach { $0.removeFromSuperview() }
cell.addSubview(button)
cell.buttonable = button
button.pinLeading()
button.pinTrailing()
button.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -17,10 +17,12 @@ class ButtonCollectionViewRow {
func add(attribute: ButtonLayoutAttributes) {
attributes.append(attribute)
}
var hasButtons: Bool {
attributes.contains(where: { $0.isButton })
}
var buttonPercentage: CGFloat?
var rowWidth: CGFloat {
return attributes.reduce(0, { result, attribute -> CGFloat in
@ -31,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
@ -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
@ -62,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
}
@ -80,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
}
}
@ -96,6 +156,7 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
var layoutHeight: CGFloat = 0.0
var position: ButtonPosition = .left
var rowQuantity: Int = 0
var buttonPercentage: CGFloat?
private var itemCache: [ButtonLayoutAttributes] = []
@ -167,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)
@ -196,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
}
@ -205,20 +266,15 @@ 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
}
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] = []

View File

@ -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()
}
}

View File

@ -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()
}
@ -159,11 +155,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()

View File

@ -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
//--------------------------------------------------

View File

@ -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
//--------------------------------------------------

View File

@ -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
//--------------------------------------------------

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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