Merge branch 'feature/buttonGroupUpdate' into 'develop'
removed old code See merge request BPHV_MIPS/vds_ios!21
This commit is contained in:
commit
db64bfe2fd
@ -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 */,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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] = []
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -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
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -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
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user