Merge branch 'refactor/enabling' into 'develop'

fix for button

See merge request BPHV_MIPS/vds_ios!103
This commit is contained in:
Bruce, Matt R 2023-08-25 21:40:42 +00:00
commit 53c2a63dbb
47 changed files with 887 additions and 829 deletions

View File

@ -48,6 +48,7 @@
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623D2892EE950071C351 /* UIDevice.swift */; };
EA3362402892EF6C0071C351 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623F2892EF6B0071C351 /* Label.swift */; };
EA33624728931B050071C351 /* Initable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624628931B050071C351 /* Initable.swift */; };
EA471F3A2A95587500CE9E58 /* LayoutConstraintable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA471F392A95587500CE9E58 /* LayoutConstraintable.swift */; };
EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */; };
EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */; };
EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB30128DCBCA500103EE3 /* Badge.swift */; };
@ -139,6 +140,7 @@
EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */; };
EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11528A1475A00B287F5 /* RadioButtonItem.swift */; };
EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */; };
EAF978212A99035B00C2FEA9 /* Enabling.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF978202A99035B00C2FEA9 /* Enabling.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -194,6 +196,7 @@
EA33623D2892EE950071C351 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
EA33623F2892EF6B0071C351 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
EA33624628931B050071C351 /* Initable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Initable.swift; sourceTree = "<group>"; };
EA471F392A95587500CE9E58 /* LayoutConstraintable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutConstraintable.swift; sourceTree = "<group>"; };
EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorGroupHandlerBase.swift; sourceTree = "<group>"; };
EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEquatable.swift; sourceTree = "<group>"; };
EA4DB30128DCBCA500103EE3 /* Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badge.swift; sourceTree = "<group>"; };
@ -285,6 +288,7 @@
EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConfiguration.swift; sourceTree = "<group>"; };
EAF7F11528A1475A00B287F5 /* RadioButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonItem.swift; sourceTree = "<group>"; };
EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentLabelAttributeModel.swift; sourceTree = "<group>"; };
EAF978202A99035B00C2FEA9 /* Enabling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Enabling.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -487,10 +491,12 @@
EAF1FE9829D4850E00101452 /* Clickable.swift */,
EAA5EEDF28F49DB3003B3210 /* Colorable.swift */,
EA3361A9288B25E40071C351 /* Disabling.swift */,
EAF978202A99035B00C2FEA9 /* Enabling.swift */,
EA5E305929510F8B0082B959 /* EnumSubset.swift */,
EAF7F0A1289AFB3900B287F5 /* Errorable.swift */,
EA3361AE288B26310071C351 /* FormFieldable.swift */,
EA33624628931B050071C351 /* Initable.swift */,
EA471F392A95587500CE9E58 /* LayoutConstraintable.swift */,
EA985C7C297DAED300F2FF2E /* Primitive.swift */,
EAF7F0A5289B0CE000B287F5 /* Resetable.swift */,
EA3361C8289054C50071C351 /* Surfaceable.swift */,
@ -963,6 +969,7 @@
EA0D1C372A681CCE00E5C127 /* ToggleView.swift in Sources */,
EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */,
EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */,
EA471F3A2A95587500CE9E58 /* LayoutConstraintable.swift in Sources */,
EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */,
EA0D1C3B2A6AD51B00E5C127 /* Typogprahy+Styles.swift in Sources */,
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */,
@ -977,6 +984,7 @@
EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */,
EA513A952A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift in Sources */,
44604AD729CE196600E62B51 /* Line.swift in Sources */,
EAF978212A99035B00C2FEA9 /* Enabling.swift in Sources */,
EA5E3058295105A40082B959 /* Tilelet.swift in Sources */,
EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */,
EA985C1D296CD13600F2FF2E /* BundleManager.swift in Sources */,
@ -1167,7 +1175,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 39;
CURRENT_PROJECT_VERSION = 40;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@ -1204,7 +1212,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 39;
CURRENT_PROJECT_VERSION = 40;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -9,6 +9,14 @@ import Foundation
import UIKit
public func DebugLog(_ message: String, file: String = #file, line: Int = #line, function: String = #function) {
#if DEBUG
let path = (file as NSString).lastPathComponent
// using print because NSLog crashes when passing certain optionals
print("[\(path)] \(function) [Line \(line)] : \(message)")
#endif
}
/// Bundle Manager keeps all bundles together for ease of use for searching within any of them for a specific asset
public class BundleManager {
public static var shared = BundleManager()

View File

@ -143,7 +143,7 @@ public class ControlColorConfiguration: KeyColorConfigurable {
///Meant to be used with any object that implements Surfaceable and Disabling. More than likely this is any View.
public class ViewColorConfiguration: KeyColorConfigurable {
public typealias KeyType = Bool
public typealias ObjectType = Surfaceable & Disabling
public typealias ObjectType = Surfaceable & Enabling
public var keyColors: [KeyColorConfiguration<KeyType>] = []
public required init() { }
@ -161,7 +161,7 @@ public class ViewColorConfiguration: KeyColorConfigurable {
/// - Parameter object: Object that implements Surfaceable and Disabling
/// - Returns: UIColor correspoding to either true/false for the disabled state and surface
public func getColor(_ object: ObjectType) -> UIColor {
if let keyColor = keyColors.first(where: {$0.key == object.disabled }) {
if let keyColor = keyColors.first(where: {$0.key == !object.isEnabled }) {
return keyColor.surfaceConfig.getColor(object)
} else {
return .clear //default

View File

@ -40,19 +40,12 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
/// Current Surface and this is used to pass down to child objects that implement Surfacable
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
/// Whether this object is disabled or not
open var disabled: Bool {
get { !isEnabled }
set {
if !isEnabled != newValue {
isEnabled = !newValue
}
}
}
/// Whether the Control is selected or not.
open override var isSelected: Bool { didSet { setNeedsUpdate() } }
/// Whether the Control can handle the isHighlighted state.
open var canHighlight: Bool = true
open var touchUpInsideCount: Int = 0
var isHighlightAnimating = false
@ -60,7 +53,7 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
/// Whether the Control is highlighted or not..
open override var isHighlighted: Bool {
didSet {
if isHighlightAnimating == false && touchUpInsideCount > 0 {
if canHighlight && isHighlightAnimating == false && touchUpInsideCount > 0 {
isHighlightAnimating = true
UIView.animate(withDuration: 0.1, animations: { [weak self] in
self?.setNeedsUpdate()
@ -76,7 +69,12 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
}
/// Whether the Control is enabled or not.
open override var isEnabled: Bool { didSet { setNeedsUpdate(); isUserInteractionEnabled = isEnabled } }
open override var isEnabled: Bool {
didSet {
setNeedsUpdate()
//isUserInteractionEnabled = isEnabled
}
}
//--------------------------------------------------
// MARK: - Initializers
@ -137,7 +135,7 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
open func reset() {
backgroundColor = .clear
surface = .light
disabled = false
isEnabled = true
}
//--------------------------------------------------

View File

@ -27,13 +27,11 @@ open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
}
}
}
/// Whether this object is disabled or not
override open var disabled: Bool {
/// Whether this object is enabled or not
override open var isEnabled: Bool {
didSet {
selectorViews.forEach { handler in
handler.disabled = disabled
}
selectorViews.forEach { $0.isEnabled = isEnabled }
}
}

View File

@ -33,7 +33,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
// MARK: - Private Properties
//--------------------------------------------------
private var shouldShowError: Bool {
guard showError && !disabled && errorText?.isEmpty == false else { return false }
guard showError && isEnabled && errorText?.isEmpty == false else { return false }
return true
}
@ -191,7 +191,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
selectorView.showError = showError
selectorView.isSelected = isSelected
selectorView.isHighlighted = isHighlighted
selectorView.disabled = disabled
selectorView.isEnabled = isEnabled
selectorView.surface = surface
}
@ -237,7 +237,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
//top label
if let labelText {
label.surface = surface
label.disabled = disabled
label.isEnabled = isEnabled
label.attributes = labelTextAttributes
label.text = labelText
label.isHidden = false
@ -252,7 +252,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
if let childText {
childLabel.text = childText
childLabel.surface = surface
childLabel.disabled = disabled
childLabel.isEnabled = isEnabled
childLabel.attributes = childTextAttributes
childLabel.isHidden = false
@ -276,7 +276,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
if let errorText, shouldShowError {
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
errorLabel.isEnabled = isEnabled
mainStackView.spacing = 8
errorLabel.isHidden = false
} else {

View File

@ -33,18 +33,8 @@ open class View: UIView, ViewProtocol, UserInfoable {
/// Current Surface and this is used to pass down to child objects that implement Surfacable
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
/// Whether this object is disabled or not.
open var disabled: Bool {
get { !isEnabled }
set {
if !isEnabled != newValue {
isEnabled = !newValue
}
}
}
/// Whether the View is enabled or not.
open var isEnabled: Bool = true { didSet { setNeedsUpdate(); isUserInteractionEnabled = isEnabled } }
open var isEnabled: Bool = true { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Initializers
@ -101,7 +91,7 @@ open class View: UIView, ViewProtocol, UserInfoable {
open func reset() {
backgroundColor = .clear
surface = .light
disabled = false
isEnabled = true
}
}

View File

@ -27,9 +27,11 @@ open class Badge: View {
/// Label used to render text
open var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.adjustsFontSizeToFitWidth = false
$0.lineBreakMode = .byTruncatingTail
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
$0.setContentHuggingPriority(.defaultHigh, for: .horizontal)
$0.textPosition = .left
$0.textStyle = .boldBodySmall
}
@ -37,7 +39,7 @@ open class Badge: View {
/// This will render the badges fill color based on the available options.
/// When used in conjunction with the surface prop, this fill color will change its tint automatically based on a light or dark surface.
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
/// The text that will be shown in the label.
open var text: String = "" { didSet { setNeedsUpdate() }}
@ -46,16 +48,27 @@ open class Badge: View {
/// This will restrict the badge height to a specific number of lines. If the text overflows the allowable space, ellipsis will show.
open var numberOfLines: Int = 1 { didSet { setNeedsUpdate() }}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private var maxWidthConstraint: NSLayoutConstraint?
private var minWidthConstraint: NSLayoutConstraint?
private func updateMaxWidth() {
maxWidthConstraint?.isActive = false
guard let maxWidth, maxWidth > minWidth else { return }
maxWidthConstraint?.constant = maxWidth
maxWidthConstraint?.isActive = true
}
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
private var minWidth: CGFloat = 23.0
private var labelInset: UIEdgeInsets = .init(top: 2,
left: VDSLayout.Spacing.space1X.value,
bottom: 2,
right: VDSLayout.Spacing.space1X.value)
/// ColorConfiguration that is mapped to the 'fillColor' for the surface.
private var backgroundColorConfiguration: AnyColorable = {
@ -96,23 +109,23 @@ open class Badge: View {
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func setup() {
super.setup()
accessibilityElements = [label]
layer.cornerRadius = 2
addSubview(label)
label.pinToSuperView(.init(top: 2,
left: VDSLayout.Spacing.space1X.value,
bottom: 2,
right: VDSLayout.Spacing.space1X.value))
maxWidthConstraint = label.widthAnchor.constraint(lessThanOrEqualToConstant: 100)
minWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 23)
minWidthConstraint?.isActive = true
label
.pinTop(labelInset.top)
.pinLeading(labelInset.left)
.pinTrailing(labelInset.right)
.pinBottom(labelInset.bottom, .defaultHigh)
label.widthGreaterThanEqualTo(constant: minWidth)
maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false }
}
/// Resets to default settings.
@ -136,20 +149,14 @@ open class Badge: View {
super.updateView()
updateTextColorConfig()
backgroundColor = backgroundColorConfiguration.getColor(self)
updateMaxWidth()
backgroundColor = backgroundColorConfiguration.getColor(self)
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
label.numberOfLines = numberOfLines
label.text = text
label.surface = surface
label.disabled = disabled
if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
maxWidthConstraint?.constant = maxWidth
maxWidthConstraint?.isActive = true
} else {
maxWidthConstraint?.isActive = false
}
label.isEnabled = isEnabled
}
}

View File

@ -258,19 +258,12 @@ open class BadgeIndicator: View {
badgeView.addSubview(label)
accessibilityElements = [label]
heightConstraint = badgeView.heightAnchor.constraint(greaterThanOrEqualToConstant: badgeSize)
heightConstraint?.isActive = true
widthConstraint = badgeView.widthAnchor.constraint(greaterThanOrEqualToConstant: badgeSize)
widthConstraint?.isActive = true
heightConstraint = badgeView.heightGreaterThanEqualTo(constant: badgeSize)
widthConstraint = badgeView.widthGreaterThanEqualTo(constant: badgeSize)
//we are insetting the padding to compensate for the border
NSLayoutConstraint.activate([
badgeView.topAnchor.constraint(equalTo: topAnchor, constant: borderWidth),
badgeView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -borderWidth),
badgeView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: borderWidth),
badgeView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -borderWidth)])
badgeView.pinToSuperView(.init(top: borderWidth, left: borderWidth, bottom: borderWidth, right: borderWidth))
labelContraints.topConstraint = label.pinTopGreaterThanOrEqualTo(anchor: badgeView.topAnchor)
labelContraints.bottomConstraint = label.pinBottomGreaterThanOrEqualTo(anchor: badgeView.bottomAnchor)
labelContraints.leadingConstraint = label.pinLeadingGreaterThanOrEqualTo(anchor: badgeView.leadingAnchor)
@ -348,7 +341,7 @@ open class BadgeIndicator: View {
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
label.text = getText()
label.surface = surface
label.disabled = disabled
label.isEnabled = isEnabled
label.sizeToFit()
setNeedsLayout()
layoutIfNeeded()

View File

@ -22,9 +22,6 @@ open class Button: ButtonBase, Useable {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var minWidthConstraint: NSLayoutConstraint?
private var widthConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?
private var initialSetupPerformed = false
//--------------------------------------------------
@ -36,7 +33,18 @@ open class Button: ButtonBase, Useable {
open var size: ButtonSize = .large { didSet { setNeedsUpdate() }}
open var width: CGFloat? { didSet { setNeedsUpdate() }}
private var _width: CGFloat? = nil
open var width: CGFloat? {
get { _width }
set {
if let newValue, newValue > size.minimumWidth {
_width = newValue
} else {
_width = nil
}
setNeedsUpdate()
}
}
open override var textColor: UIColor {
textColorConfiguration.getColor(self)
@ -120,14 +128,6 @@ open class Button: ButtonBase, Useable {
super.setup()
isAccessibilityElement = true
accessibilityTraits = .button
//only 1 of the 2 widths can be on at the same time
widthConstraint = widthAnchor.constraint(equalToConstant: 0)
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth)
//height
heightConstraint = heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
/// Resets to default settings.
@ -145,7 +145,12 @@ open class Button: ButtonBase, Useable {
// MARK: - Overrides
//--------------------------------------------------
open override var intrinsicContentSize: CGSize {
guard let width, width > 0 else { return super.intrinsicContentSize }
guard let width, width > 0 else {
var superSize = super.intrinsicContentSize
superSize.height = size.height
return superSize
}
return CGSize(width: width > size.minimumWidth ? width : size.minimumWidth, height: size.height)
}
@ -156,9 +161,7 @@ open class Button: ButtonBase, Useable {
let bgColor = backgroundColorConfiguration.getColor(self)
let borderColor = borderColorConfiguration.getColor(self)
let borderWidth = use == .secondary ? VDSFormControls.widthBorder : 0.0
let buttonHeight = size.height
let cornerRadius = size.cornerRadius
let minWidth = size.minimumWidth
let edgeInsets = size.edgeInsets
backgroundColor = bgColor
@ -166,18 +169,8 @@ open class Button: ButtonBase, Useable {
layer.cornerRadius = cornerRadius
layer.borderWidth = borderWidth
contentEdgeInsets = edgeInsets
minWidthConstraint?.constant = minWidth
heightConstraint?.constant = buttonHeight
if let width, width > minWidth {
widthConstraint?.constant = width
widthConstraint?.isActive = true
minWidthConstraint?.isActive = false
} else {
widthConstraint?.isActive = false
minWidthConstraint?.isActive = true
}
invalidateIntrinsicContentSize()
}
}

View File

@ -11,7 +11,7 @@ import VDSColorTokens
import VDSFormControlsTokens
import Combine
public protocol Buttonable: UIControl, Surfaceable, Disabling {
public protocol Buttonable: UIControl, Surfaceable, Enabling {
var availableSizes: [ButtonSize] { get }
var text: String? { get set }
var intrinsicContentSize: CGSize { get }
@ -83,19 +83,9 @@ open class ButtonBase: UIButton, Buttonable, ViewProtocol, UserInfoable, Clickab
}
}
}
/// Whether this object is disabled or not
open var disabled: Bool {
get { !isEnabled }
set {
if !isEnabled != newValue {
isEnabled = !newValue
}
}
}
/// Whether the Control is enabled or not.
open override var isEnabled: Bool { didSet { setNeedsUpdate(); isUserInteractionEnabled = isEnabled } }
open override var isEnabled: Bool { didSet { setNeedsUpdate() } }
open var textStyle: TextStyle { .defaultStyle }
@ -144,7 +134,7 @@ open class ButtonBase: UIButton, Buttonable, ViewProtocol, UserInfoable, Clickab
open func reset() {
shouldUpdateView = false
surface = .light
disabled = false
isEnabled = true
text = nil
accessibilityCustomActions = []
shouldUpdateView = true

View File

@ -93,13 +93,10 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// Whether this object is disabled or not
override open var disabled: Bool {
/// Whether this object is enabled or not
override open var isEnabled: Bool {
didSet {
buttons.forEach { button in
var b = button
b.disabled = disabled
}
buttons.forEach { $0.isEnabled = isEnabled }
}
}
@ -130,12 +127,13 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
//--------------------------------------------------
// MARK: - Public Functions
//--------------------------------------------------
open override func setup() {
super.setup()
addSubview(collectionView)
collectionView.pinToSuperView()
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
@ -150,11 +148,12 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
open override func layoutSubviews() {
super.layoutSubviews()
// Accounts for any collection size changes
DispatchQueue.main.async {
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
//--------------------------------------------------
// MARK: - UICollectionViewDataSource
//--------------------------------------------------
@ -195,5 +194,4 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
public func collectionView(_ collectionView: UICollectionView, buttonableAtIndexPath indexPath: IndexPath) -> Buttonable {
buttons[indexPath.row]
}
}

View File

@ -18,6 +18,10 @@ open class TextLink: ButtonBase {
// MARK: - Private Properties
//--------------------------------------------------
private var lineHeightConstraint: NSLayoutConstraint?
private var line = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
//--------------------------------------------------
// MARK: - Properties
@ -64,10 +68,6 @@ open class TextLink: ButtonBase {
super.init(coder: coder)
}
private var line = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
//--------------------------------------------------
// MARK: - Public Functions
//--------------------------------------------------
@ -75,15 +75,15 @@ open class TextLink: ButtonBase {
super.setup()
isAccessibilityElement = true
accessibilityTraits = .link
if let titleLabel {
addSubview(line)
line.pinLeading(titleLabel.leadingAnchor)
line.pinTrailing(titleLabel.trailingAnchor)
line.pinTop(titleLabel.bottomAnchor)
line.pinBottom(bottomAnchor)
lineHeightConstraint = line.heightAnchor.constraint(equalToConstant: 1.0)
line.pinBottom(bottomAnchor, 0, .defaultHigh)
lineHeightConstraint = line.height(constant: 1)
lineHeightConstraint?.isActive = true
titleLabel.debugBorder(show: true)
}
}
@ -92,7 +92,7 @@ open class TextLink: ButtonBase {
super.reset()
shouldUpdateView = false
text = nil
size = .large
size = .large
accessibilityCustomActions = []
isAccessibilityElement = true
accessibilityTraits = .link

View File

@ -119,7 +119,7 @@ extension TextLinkCaret {
}
func setAttribute(on attributedString: NSMutableAttributedString) {
let imageAttr = ImageLabelAttribute(location: location, imageName: "\(position.rawValue)-caret-bold", frame: .init(x: 0, y: 0, width: caretSize.width, height: caretSize.height), tintColor: tintColor)
let imageAttr = ImageLabelAttribute(location: location, imageName: "\(position.rawValue)-caret-bold", frame: .init(x: 0, y: 0, width: caretSize.width, height: caretSize.height), tintColor: tintColor, accessibleText: "Caret")
let spacer = NSAttributedString.spacer(for: spacerWidth)
guard let image = try? imageAttr.getAttachment() else { return }

View File

@ -99,7 +99,7 @@ open class Checkbox: SelectorBase {
shapeLayer?.removeAllAnimations()
if isAnimated && !disabled && !isHighlighted {
if isAnimated && isEnabled && !isHighlighted {
let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear)
animateStrokeEnd.duration = 0.3

View File

@ -40,7 +40,7 @@ open class CheckboxGroup: SelectorGroupHandlerBase<CheckboxItem> {
if let selectorModels {
selectorViews = selectorModels.enumerated().map { index, model in
return CheckboxItem().with {
$0.disabled = model.disabled
$0.isEnabled = !model.disabled
$0.surface = model.surface
$0.inputId = model.inputId
$0.value = model.value
@ -110,7 +110,7 @@ open class CheckboxGroup: SelectorGroupHandlerBase<CheckboxItem> {
}
extension CheckboxGroup {
public struct CheckboxModel : Surfaceable, Disabling, Initable, FormFieldable, Errorable {
public struct CheckboxModel : Surfaceable, Initable, FormFieldable, Errorable {
/// Whether this object is disabled or not
public var disabled: Bool

View File

@ -235,22 +235,17 @@ open class ButtonIcon: Control {
addSubview(icon)
//determines the height/width of the icon
layoutGuideWidthConstraint = iconLayoutGuide.widthAnchor.constraint(equalToConstant: size.containerSize)
layoutGuideHeightConstraint = iconLayoutGuide.heightAnchor.constraint(equalToConstant: size.containerSize)
layoutGuideWidthConstraint = iconLayoutGuide.width(constant: size.containerSize)
layoutGuideHeightConstraint = iconLayoutGuide.height(constant: size.containerSize)
//pin layout guide
iconLayoutGuide.pinToSuperView()
//determines the center point of the icon
centerXConstraint = icon.centerXAnchor.constraint(equalTo: iconLayoutGuide.centerXAnchor, constant: 0)
centerXConstraint?.activate()
centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0)
//activate the constraints
NSLayoutConstraint.activate([layoutGuideWidthConstraint!,
layoutGuideHeightConstraint!,
centerXConstraint!,
centerYConstraint!,
iconLayoutGuide.topAnchor.constraint(equalTo: topAnchor),
iconLayoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
iconLayoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
iconLayoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
centerYConstraint?.activate()
}
/// Resets to default settings.

View File

@ -16,8 +16,10 @@ open class Icon: View {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var widthConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?
private var dimensions: CGSize {
guard let customSize else { return size.dimensions }
return .init(width: customSize, height: customSize)
}
//--------------------------------------------------
// MARK: - Public Properties
@ -42,20 +44,20 @@ open class Icon: View {
//functions
//--------------------------------------------------
// MARK: - Lifecycle
// MARK: - Overrides
//--------------------------------------------------
open override func setup() {
super.setup()
setContentCompressionResistancePriority(.required, for: .vertical)
setContentHuggingPriority(.required, for: .vertical)
setContentCompressionResistancePriority(.required, for: .horizontal)
setContentHuggingPriority(.required, for: .horizontal)
addSubview(imageView)
imageView.pinToSuperView()
heightConstraint = imageView.heightAnchor.constraint(equalToConstant: size.dimensions.height)
heightConstraint?.isActive = true
widthConstraint = imageView.widthAnchor.constraint(equalToConstant: size.dimensions.width)
widthConstraint?.isActive = true
backgroundColor = .clear
isAccessibilityElement = true
@ -68,6 +70,10 @@ open class Icon: View {
color = VDSColor.paletteBlack
imageView.image = nil
}
open override var intrinsicContentSize: CGSize {
dimensions
}
/// Function used to make changes to the View based off a change events or from local properties.
open override func updateView() {
@ -81,17 +87,7 @@ open class Icon: View {
} else if surface == .light && color == VDSColor.paletteBlack {
imageColor = VDSColor.elementsPrimaryOnlight
}
//set the icon dimensions
var dimensions = size.dimensions
if let customSize {
dimensions = .init(width: customSize, height: customSize)
}
heightConstraint?.constant = dimensions.height
widthConstraint?.constant = dimensions.width
//get the image name
//set the image
if let name, let image = getImage(for: name.rawValue) {
@ -99,8 +95,10 @@ open class Icon: View {
} else {
imageView.image = nil
}
}
invalidateIntrinsicContentSize()
}
private func getImage(for imageName: String) -> UIImage? {
return BundleManager.shared.image(for: imageName)

View File

@ -38,18 +38,17 @@ public struct ActionLabelAttribute: ActionLabelAttributeModel {
public var length: Int
public var shouldUnderline: Bool
public var accessibleText: String?
public var action: PassthroughSubject<Void, Never>
public var action = PassthroughSubject<Void, Never>()
public var subscriber: AnyCancellable?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(location: Int, length: Int, shouldUnderline: Bool = true, accessibleText: String? = nil, action: PassthroughSubject<Void, Never> = .init() ) {
public init(location: Int, length: Int, shouldUnderline: Bool = true, accessibleText: String? = nil) {
self.location = location
self.length = length
self.shouldUnderline = shouldUnderline
self.accessibleText = accessibleText
self.action = action
}
private enum CodingKeys: String, CodingKey {
@ -67,3 +66,19 @@ public struct ActionLabelAttribute: ActionLabelAttributeModel {
extension NSAttributedString.Key {
public static let action = NSAttributedString.Key(rawValue: "action")
}
extension String {
public func nsRange(of text: String) -> NSRange? {
guard let found = range(of: text) else { return nil }
return NSRange(found, in: self)
}
}
extension ActionLabelAttribute {
public init? (text: String, linkText: String, accessibleText: String? = nil) {
guard let range = text.nsRange(of: linkText) else { return nil }
self.init(location: range.location, length: range.length)
}
}

View File

@ -28,7 +28,8 @@ public struct ImageLabelAttribute: AttachmentLabelAttributeModel {
public var image: UIImage?
public var frame: CGRect?
public var tintColor: UIColor?
public var accessibleText: String?
//--------------------------------------------------
// MARK: - Equatable
//--------------------------------------------------
@ -40,11 +41,27 @@ public struct ImageLabelAttribute: AttachmentLabelAttributeModel {
return id == equatable.id && range == equatable.range && imageName == equatable.imageName
}
public init(id: UUID = UUID(), location: Int, imageName: String? = nil, image: UIImage? = nil, frame: CGRect? = nil, tintColor: UIColor? = nil, accessibleText: String? = nil) {
self.id = id
self.location = location
self.imageName = imageName
self.image = image
self.frame = frame
self.tintColor = tintColor
self.accessibleText = accessibleText
}
//--------------------------------------------------
// MARK: - Private Functions
//--------------------------------------------------
private func imageAttachment(image: UIImage) -> NSTextAttachment {
let attachment = NSTextAttachment()
if let accessibleText {
attachment.accessibilityLabel = accessibleText
attachment.isAccessibilityElement = true
} else {
attachment.isAccessibilityElement = false
}
attachment.image = tintColor != nil ? image.withTintColor(tintColor!) : image
if let frame {
attachment.bounds = frame

View File

@ -68,9 +68,8 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
addHandler(on: attributedString)
}
public init(id: UUID = UUID(), action: PassthroughSubject<Void, Never> = PassthroughSubject<Void, Never>(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, presenter: UIView? = nil) {
public init(id: UUID = UUID(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, presenter: UIView? = nil) {
self.id = id
self.action = action
self.subscriber = subscriber
self.surface = surface
self.accessibleText = accessibleText

View File

@ -44,25 +44,19 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
open var userInfo = [String: Primitive]()
open override var numberOfLines: Int { didSet { setNeedsUpdate() }}
open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }}
override open var text: String? {
didSet {
attributes = nil
setNeedsUpdate()
}
}
/// Whether this object is disabled or not
open var disabled: Bool {
get { !isEnabled }
set {
if !isEnabled != newValue {
isEnabled = !newValue
}
}
}
/// Whether the View is enabled or not.
open override var isEnabled: Bool { didSet { setNeedsUpdate(); isUserInteractionEnabled = isEnabled } }
open override var isEnabled: Bool { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Configuration Properties
@ -119,7 +113,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
open func reset() {
shouldUpdateView = false
surface = .light
disabled = false
isEnabled = true
attributes = nil
textStyle = .defaultStyle
textPosition = .left

View File

@ -19,15 +19,16 @@ open class Line: View {
case primary, secondary
}
public enum Orientation: String, CaseIterable {
case horizontal, vertical
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var lineView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
open var style: Style = .primary { didSet { setNeedsUpdate() } }
open var orientation: Orientation = .horizontal { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
@ -37,29 +38,37 @@ open class Line: View {
config.setSurfaceColors(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark, forKey: .secondary)
return config.eraseToAnyColorable()
}()
//--------------------------------------------------
// MARK: - Lifecycle
// MARK: - Overrides
//--------------------------------------------------
open override func setup() {
super.setup()
addSubview(lineView)
lineView.height(1)
lineView.pinToSuperView()
open override var intrinsicContentSize: CGSize {
if orientation == .vertical {
return .init(width: 1, height: bounds.height)
} else {
return .init(width: bounds.width, height: 1)
}
}
/// Resets to default settings.
open override func reset() {
super.reset()
style = .primary
orientation = .horizontal
}
open override func setup() {
super.setup()
}
/// Function used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
lineView.backgroundColor = lineViewColorConfiguration.getColor(self)
backgroundColor = lineViewColorConfiguration.getColor(self)
invalidateIntrinsicContentSize()
}
}

View File

@ -27,7 +27,14 @@ open class Loader: View {
open var isActive: Bool = true { didSet { setNeedsUpdate() } }
/// The Int used to determine the height and width of the Loader
open var size: Int = 40 { didSet { setNeedsUpdate() } }
open var size: Int = 40 {
didSet {
setNeedsUpdate();
invalidateIntrinsicContentSize()
}
}
open override var intrinsicContentSize: CGSize { .init(width: size, height: size) }
//--------------------------------------------------
// MARK: - Lifecycle
@ -36,13 +43,15 @@ open class Loader: View {
super.setup()
addSubview(icon)
icon
.pinTopGreaterThanOrEqualTo()
.pinLeadingGreaterThanOrEqualTo()
.pinTrailingLessThanOrEqualTo()
.pinBottomLessThanOrEqualTo()
NSLayoutConstraint.activate([
icon.centerXAnchor.constraint(equalTo: centerXAnchor),
icon.centerYAnchor.constraint(equalTo: centerYAnchor),
icon.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
icon.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor),
icon.topAnchor.constraint(greaterThanOrEqualTo: topAnchor),
icon.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor)
icon.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}

View File

@ -43,7 +43,7 @@ open class RadioBoxGroup: SelectorGroupSelectedHandlerBase<RadioBoxItem> {
$0.subTextAttributes = model.subTextAttributes
$0.subTextRight = model.subText
$0.subTextRightAttributes = model.subTextAttributes
$0.disabled = model.disabled
$0.isEnabled = !model.disabled
$0.inputId = model.inputId
$0.isSelected = model.selected
}
@ -106,7 +106,7 @@ open class RadioBoxGroup: SelectorGroupSelectedHandlerBase<RadioBoxItem> {
}
extension RadioBoxGroup {
public struct RadioBoxModel: Surfaceable, Initable, Disabling, FormFieldable {
public struct RadioBoxModel: Surfaceable, Initable, FormFieldable {
/// Whether this object is disabled or not
public var disabled: Bool
/// Current Surface and this is used to pass down to child objects that implement Surfacable

View File

@ -187,14 +187,14 @@ open class RadioBoxItem: Control, Changeable {
//text label
textLabel.text = text
textLabel.surface = surface
textLabel.disabled = disabled
textLabel.isEnabled = isEnabled
textLabel.attributes = textAttributes
//subText label
if let subText {
subTextLabel.text = subText
subTextLabel.surface = surface
subTextLabel.disabled = disabled
subTextLabel.isEnabled = isEnabled
subTextLabel.attributes = subTextAttributes
subTextLabel.isHidden = false
@ -209,7 +209,7 @@ open class RadioBoxItem: Control, Changeable {
if let subTextRight {
subTextRightLabel.text = subTextRight
subTextRightLabel.surface = surface
subTextRightLabel.disabled = disabled
subTextRightLabel.isEnabled = isEnabled
subTextRightLabel.attributes = subTextRightAttributes
subTextRightLabel.isHidden = false

View File

@ -35,7 +35,7 @@ open class RadioButtonGroup: SelectorGroupSelectedHandlerBase<RadioButtonItem> {
if let selectorModels {
selectorViews = selectorModels.enumerated().map { index, model in
return RadioButtonItem().with {
$0.disabled = model.disabled
$0.isEnabled = !model.disabled
$0.surface = model.surface
$0.inputId = model.inputId
$0.value = model.value
@ -114,7 +114,7 @@ open class RadioButtonGroup: SelectorGroupSelectedHandlerBase<RadioButtonItem> {
}
extension RadioButtonGroup {
public struct RadioButtonModel: Surfaceable, Disabling, Initable, FormFieldable, Errorable {
public struct RadioButtonModel: Surfaceable, Initable, FormFieldable, Errorable {
/// Whether this object is disabled or not
public var disabled: Bool

View File

@ -176,13 +176,13 @@ open class RadioSwatch: Control {
var fillColorBackground: UIColor = .clear
if let fillImage {
fillView.image = disabled ? fillImage.image(alpha: disabledAlpha) : fillImage
fillView.image = !isEnabled ? fillImage.image(alpha: disabledAlpha) : fillImage
} else {
fillView.image = nil
if let primary = primaryColor, let secondary = secondaryColor {
let firstColor = disabled ? primary.withAlphaComponent(disabledAlpha) : primary
let secondColor = disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
let firstColor = !isEnabled ? primary.withAlphaComponent(disabledAlpha) : primary
let secondColor = !isEnabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
let gradient = CAGradientLayer()
gradientLayer = gradient
gradient.frame = fillView.bounds
@ -195,7 +195,7 @@ open class RadioSwatch: Control {
}
}
fillView.backgroundColor = disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground
fillView.backgroundColor = !isEnabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground
fillView.layer.borderColor = fillBorderColor.cgColor
fillView.layer.cornerRadius = fillView.bounds.width * 0.5
fillView.layer.borderWidth = selectorBorderWidth

View File

@ -32,7 +32,7 @@ open class RadioSwatchGroup: SelectorGroupSelectedHandlerBase<RadioSwatch>, UICo
$0.primaryColor = model.primaryColor
$0.secondaryColor = model.secondaryColor
$0.strikethrough = model.strikethrough
$0.disabled = model.disabled
$0.isEnabled = !model.disabled
$0.surface = model.surface
$0.inputId = model.inputId
$0.value = model.value
@ -71,15 +71,15 @@ open class RadioSwatchGroup: SelectorGroupSelectedHandlerBase<RadioSwatch>, UICo
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// Whether this object is disabled or not
override public var disabled: Bool {
override public var isEnabled: Bool {
didSet {
for selector in selectorViews {
selector.disabled = disabled
}
selectorViews.forEach { $0.isEnabled = isEnabled }
collectionView.reloadData()
}
}
/// Current Surface and this is used to pass down to child objects that implement Surfacable
override public var surface: Surface {
didSet {
@ -132,7 +132,7 @@ open class RadioSwatchGroup: SelectorGroupSelectedHandlerBase<RadioSwatch>, UICo
label.textStyle = .bodySmall
label.text = selectedHandler?.text ?? " "
label.surface = surface
label.disabled = disabled
label.isEnabled = isEnabled
collectionView.reloadData()
}
@ -151,7 +151,7 @@ open class RadioSwatchGroup: SelectorGroupSelectedHandlerBase<RadioSwatch>, UICo
// MARK: - UICollectionViewDelegate
//--------------------------------------------------
open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return !selectorViews[indexPath.row].disabled
return selectorViews[indexPath.row].isEnabled
}
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
@ -193,7 +193,7 @@ open class RadioSwatchGroup: SelectorGroupSelectedHandlerBase<RadioSwatch>, UICo
}
extension RadioSwatchGroup {
public struct RadioSwatchModel: Surfaceable, Disabling, Initable {
public struct RadioSwatchModel: Surfaceable, Initable {
/// Whether this object is disabled or not
public var disabled: Bool = false
/// Current Surface and this is used to pass down to child objects that implement Surfacable

View File

@ -95,12 +95,11 @@ extension Tabs {
private let layoutGuide = UILayoutGuide()
private var widthConstraint: NSLayoutConstraint? {
if let width, orientation == .vertical {
return layoutGuide.widthAnchor.constraint(equalToConstant: width)
} else {
return layoutGuide.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidth)
}
private func updateWidth() {
labelWidthConstraint?.isActive = false
guard let width, width > minWidth else { return }
labelWidthConstraint?.constant = width
labelWidthConstraint?.isActive = true
}
//--------------------------------------------------
@ -124,27 +123,26 @@ extension Tabs {
open override func setup() {
super.setup()
canHighlight = false
addLayoutGuide(layoutGuide)
addSubview(label)
accessibilityTraits = .button
isAccessibilityElement = true
//activate the constraints
NSLayoutConstraint.activate([layoutGuide.topAnchor.constraint(equalTo: topAnchor),
layoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
layoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
layoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
//pin layoutguide
layoutGuide.pinToSuperView()
//pin trailing
label.pinTrailing(layoutGuide.trailingAnchor)
labelTopConstraint = label.topAnchor.constraint(equalTo: layoutGuide.topAnchor)
labelTopConstraint?.isActive = true
labelBottomConstraint = label.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor)
labelBottomConstraint?.isActive = true
labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor)
labelLeadingConstraint?.isActive = true
//setup constraints
labelWidthConstraint = layoutGuide.width(constant: 0).with { $0.isActive = false }
layoutGuide.widthGreaterThanEqualTo(minWidth)
labelTopConstraint = label.pinTop(anchor: layoutGuide.topAnchor)
labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor)
labelBottomConstraint = label.pinBottom(anchor: layoutGuide.bottomAnchor, priority: .defaultHigh)
}
/// Function used to make changes to the View based off a change events or from local properties.
@ -156,9 +154,7 @@ extension Tabs {
accessibilityIdentifier = "VDSTab:\(text)"
//constaints
labelWidthConstraint?.isActive = false
labelWidthConstraint = widthConstraint
labelWidthConstraint?.isActive = true
updateWidth()
labelLeadingConstraint?.constant = leadingSpace
labelTopConstraint?.constant = otherSpace
labelBottomConstraint?.constant = -otherSpace
@ -169,7 +165,6 @@ extension Tabs {
label.textStyle = textStyle
label.textPosition = textPosition
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
setNeedsLayout()
layoutIfNeeded()

View File

@ -144,6 +144,10 @@ open class Tabs: View {
orientation == .horizontal && fillContainer ? .center : .left
}
private var applyOverflow: Bool {
orientation == .horizontal && overflow == .scroll && !fillContainer
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
@ -187,20 +191,20 @@ open class Tabs: View {
contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true
borderlineViewWidthConstraint = borderlineView.widthAnchor.constraint(equalToConstant: 0)
borderlineViewHeightConstraint = borderlineView.heightAnchor.constraint(equalToConstant: 0)
borderlineViewWidthConstraint = borderlineView.width(constant: 0)
borderlineViewHeightConstraint = borderlineView.height(constant: 0)
borderlineViewLeadingConstraint = borderlineView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
borderlineViewTrailingConstraint = borderlineView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
borderlineViewTopConstraint = borderlineView.topAnchor.constraint(equalTo: contentView.topAnchor)
borderlineViewBottomConstraint = borderlineView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
borderlineViewLeadingConstraint = borderlineView.pinLeading(anchor: contentView.leadingAnchor)
borderlineViewTrailingConstraint = borderlineView.pinTrailing(anchor: contentView.trailingAnchor)
borderlineViewTopConstraint = borderlineView.pinTop(anchor: contentView.topAnchor)
borderlineViewBottomConstraint = borderlineView.pinBottom(anchor: contentView.bottomAnchor)
}
/// Function used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
updateStackView()
updateTabs()
updateContentView()
@ -258,7 +262,9 @@ open class Tabs: View {
/// Updates the Tab individual views from local properties.
private func updateTabs() {
let numberOfLines = applyOverflow ? 1 : 0
for (index, tabItem) in tabViews.enumerated() {
tabItem.label.numberOfLines = numberOfLines
tabItem.size = size
tabItem.isSelected = selectedIndex == index
tabItem.index = index
@ -277,10 +283,10 @@ open class Tabs: View {
contentViewWidthConstraint?.isActive = false
// Apply overflow
if orientation == .horizontal && overflow == .scroll && !fillContainer {
let contentWidth = tabStackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
if applyOverflow {
contentViewWidthConstraint = nil
scrollView.contentSize = CGSize(width: contentWidth, height: scrollView.bounds.height)
scrollView.contentSize = CGSize(width: tabStackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width,
height: scrollView.bounds.height)
} else {
contentViewWidthConstraint = contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
scrollView.contentSize = bounds.size
@ -294,6 +300,11 @@ open class Tabs: View {
scrollToSelectedIndex(animated: true)
}
open override func layoutSubviews() {
super.layoutSubviews()
updateContentView()
}
//update layout for borderline
private func updateBorderline() {
//borderLine

View File

@ -132,12 +132,7 @@ open class TabsContainer: View {
stackView.addArrangedSubview(tabMenu)
stackView.addArrangedSubview(contentView)
NSLayoutConstraint.activate([
tabMenuLayoutGuide.topAnchor.constraint(equalTo: topAnchor),
tabMenuLayoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
tabMenuLayoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
tabMenuLayoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)
])
tabMenuLayoutGuide.pinToSuperView()
}
/// Function used to make changes to the View based off a change events or from local properties.
@ -168,7 +163,7 @@ open class TabsContainer: View {
contentViewWidthConstraint?.isActive = true
tabMenu.surface = surface
tabMenu.disabled = disabled
tabMenu.isEnabled = isEnabled
tabMenu.orientation = orientation
tabMenu.borderLine = borderLine
tabMenu.fillContainer = fillContainer

View File

@ -283,7 +283,7 @@ open class EntryField: Control, Changeable {
//dealing with the "Optional" addition to the text
if let oldText = updatedLabelText, !required, !oldText.hasSuffix("Optional") {
if !disabled {
if isEnabled {
let optionColorAttr = ColorLabelAttribute(location: oldText.count + 2,
length: 8,
color: VDSColor.elementsSecondaryOnlight)
@ -301,20 +301,19 @@ open class EntryField: Control, Changeable {
titleLabel.text = updatedLabelText
titleLabel.attributes = attributes
titleLabel.surface = surface
titleLabel.disabled = disabled
titleLabel.isEnabled = isEnabled
}
open func updateErrorLabel(){
if showError, let errorText {
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
errorLabel.isEnabled = isEnabled
errorLabel.isHidden = false
icon.name = .error
icon.color = VDSColor.paletteBlack
icon.surface = surface
icon.isHidden = disabled
icon.isHidden = !isEnabled
} else {
icon.isHidden = true
errorLabel.isHidden = true
@ -326,7 +325,7 @@ open class EntryField: Control, Changeable {
if let helperText {
helperLabel.text = helperText
helperLabel.surface = surface
helperLabel.disabled = disabled
helperLabel.isEnabled = isEnabled
helperLabel.isHidden = false
} else {
helperLabel.isHidden = true

View File

@ -165,7 +165,7 @@ open class InputField: EntryField, UITextFieldDelegate {
open override func updateView() {
super.updateView()
textField.isEnabled = !disabled
textField.isEnabled = isEnabled
textField.textColor = textFieldTextColorConfiguration.getColor(self)
//show error or success
@ -175,13 +175,13 @@ open class InputField: EntryField, UITextFieldDelegate {
} else if showSuccess, let successText {
successLabel.text = successText
successLabel.surface = surface
successLabel.disabled = disabled
successLabel.isEnabled = isEnabled
successLabel.isHidden = false
errorLabel.isHidden = true
icon.name = .checkmarkAlt
icon.color = VDSColor.paletteBlack
icon.surface = surface
icon.isHidden = disabled
icon.isHidden = !isEnabled
} else {
icon.isHidden = true
successLabel.isHidden = true

View File

@ -95,7 +95,7 @@ open class TextArea: EntryField {
open override func updateView() {
super.updateView()
textView.isEditable = !disabled
textView.isEditable = isEnabled
textView.textColor = textViewTextColorConfiguration.getColor(self)
//set the width constraints

View File

@ -168,14 +168,10 @@ open class TileContainer: Control {
containerView.backgroundColor = .clear
containerTopConstraint = containerView.topAnchor.constraint(equalTo: topAnchor, constant: padding.value)
containerTopConstraint?.isActive = true
containerBottomConstraint = containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: padding.value)
containerBottomConstraint?.isActive = true
containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding.value)
containerLeadingConstraint?.isActive = true
containerTrailingConstraint = containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: padding.value)
containerTrailingConstraint?.isActive = true
containerTopConstraint = containerView.pinTop(anchor: topAnchor, constant: padding.value)
containerBottomConstraint = containerView.pinBottom(anchor: bottomAnchor, constant: padding.value)
containerLeadingConstraint = containerView.pinLeading(anchor: leadingAnchor, constant: padding.value)
containerTrailingConstraint = containerView.pinTrailing(anchor: trailingAnchor, constant: padding.value)
highlightView.pinToSuperView()
highlightView.isHidden = true

View File

@ -43,7 +43,7 @@ open class TitleLockup: View {
private var stackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
$0.distribution = .fill
$0.distribution = .fillProportionally
}
private var otherStandardStyle: OtherStandardStyle {
@ -269,8 +269,11 @@ open class TitleLockup: View {
stackView.addArrangedSubview(subTitleLabel)
//pin stackview to edges
stackView.pinToSuperView()
stackView
.pinTop()
.pinLeading()
.pinTrailing()
.pinBottom(0, .defaultHigh)
}
/// Resets to default settings.
@ -311,7 +314,6 @@ open class TitleLockup: View {
eyebrowLabel.attributes = eyebrowModel.textAttributes
eyebrowLabel.numberOfLines = eyebrowModel.numberOfLines
eyebrowLabel.surface = surface
//When uniform size is true
if let titleModel, isUniformSize {
if titleModel.isBold {
@ -329,8 +331,6 @@ open class TitleLockup: View {
eyebrowLabel.textColorConfiguration = textColorPrimaryConfiguration
eyebrowLabel.textStyle = eyebrowModel.isBold ? otherStandardStyle.value.bold : otherStandardStyle.value.regular
}
} else {
eyebrowLabel.reset()
}
if let titleModel, !titleModel.text.isEmpty {
@ -341,8 +341,6 @@ open class TitleLockup: View {
titleLabel.attributes = titleModel.textAttributes
titleLabel.numberOfLines = titleModel.numberOfLines
titleLabel.surface = surface
} else {
titleLabel.reset()
}
if let subTitleModel, !subTitleModel.text.isEmpty {
@ -354,8 +352,6 @@ open class TitleLockup: View {
subTitleLabel.attributes = subTitleModel.textAttributes
subTitleLabel.numberOfLines = subTitleModel.numberOfLines
subTitleLabel.surface = surface
} else {
subTitleLabel.reset()
}
//if both first 2 rows not empty set spacing
@ -376,5 +372,5 @@ open class TitleLockup: View {
eyebrowLabel.isHidden = eyebrowTextIsEmpty
titleLabel.isHidden = titleTextIsEmpty
subTitleLabel.isHidden = subTitleTextIsEmpty
}
}
}

View File

@ -86,6 +86,7 @@ open class Toggle: Control, Changeable {
open var toggleView = ToggleView().with {
$0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
$0.isUserInteractionEnabled = false
$0.isAccessibilityElement = false
}
open var label = Label().with {
@ -140,34 +141,35 @@ open class Toggle: Control, Changeable {
open override func setup() {
super.setup()
canHighlight = false
isAccessibilityElement = true
accessibilityTraits = .button
addSubview(label)
addSubview(toggleView)
let heightEqual = heightAnchor.constraint(equalToConstant: toggleContainerSize.height)
heightEqual.priority = .defaultLow
let heightGreater = heightAnchor.constraint(greaterThanOrEqualToConstant: toggleContainerSize.height)
heightGreater.priority = .defaultHigh
label.widthLessThanEqualTo(labelMaxWidth)
// Set up initial constraints for label and switch
toggleView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
labelConstraints = [
heightEqual, heightGreater,
label.widthAnchor.constraint(lessThanOrEqualToConstant: labelMaxWidth),
height(constant: toggleContainerSize.height, priority: .defaultLow),
heightGreaterThanEqualTo(constant: toggleContainerSize.height, priority: .defaultHigh),
label.topAnchor.constraint(equalTo: topAnchor),
label.bottomAnchor.constraint(equalTo: bottomAnchor),
]
leftConstraints = [
toggleView.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: spacingBetween)
toggleView.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: spacingBetween),
label.leadingAnchor.constraint(equalTo: leadingAnchor),
toggleView.trailingAnchor.constraint(equalTo: trailingAnchor)
]
rightConstraints = [
label.leadingAnchor.constraint(equalTo: toggleView.trailingAnchor, constant: spacingBetween)
toggleView.leadingAnchor.constraint(equalTo: leadingAnchor),
label.leadingAnchor.constraint(equalTo: toggleView.trailingAnchor, constant: spacingBetween),
label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
]
}
@ -177,6 +179,7 @@ open class Toggle: Control, Changeable {
super.reset()
shouldUpdateView = false
label.reset()
isEnabled = true
isOn = false
isAnimated = true
showText = false
@ -197,7 +200,7 @@ open class Toggle: Control, Changeable {
updateLabel()
toggleView.surface = surface
toggleView.disabled = disabled
toggleView.isEnabled = isEnabled
toggleView.isOn = isOn
}
@ -223,6 +226,7 @@ open class Toggle: Control, Changeable {
private var showLabel: Bool {
showText && !statusText.isEmpty
}
private func updateLabel() {
label.isHidden = !showLabel
@ -231,7 +235,7 @@ open class Toggle: Control, Changeable {
label.textStyle = textStyle
label.text = statusText
label.surface = surface
label.disabled = disabled
label.isEnabled = isEnabled
switch textPosition {
case .left:
NSLayoutConstraint.deactivate(rightConstraints)
@ -246,7 +250,6 @@ open class Toggle: Control, Changeable {
NSLayoutConstraint.deactivate(rightConstraints)
NSLayoutConstraint.deactivate(labelConstraints)
}
invalidateIntrinsicContentSize()
}
//--------------------------------------------------

View File

@ -117,19 +117,22 @@ open class ToggleView: Control, Changeable {
isAccessibilityElement = true
accessibilityTraits = .button
addSubview(toggleView)
toggleView.addSubview(knobView)
NSLayoutConstraint.activate([
toggleView.widthAnchor.constraint(equalToConstant: toggleSize.width),
toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height),
toggleView.centerYAnchor.constraint(equalTo: centerYAnchor),
knobView.heightAnchor.constraint(equalToConstant: knobSize.height),
knobView.widthAnchor.constraint(equalToConstant: knobSize.width),
knobView.centerYAnchor.constraint(equalTo: toggleView.centerYAnchor),
knobView.topAnchor.constraint(greaterThanOrEqualTo: toggleView.topAnchor)
])
toggleView.pinToSuperView()
toggleView
.width(toggleSize.width)
.height(toggleSize.height)
knobView
.pinTopGreaterThanOrEqualTo()
.width(knobSize.width)
.height(knobSize.height)
knobView.centerYAnchor.constraint(equalTo: toggleView.centerYAnchor).activate()
// Set cornerRadius
knobView.layer.cornerRadius = knobSize.height / 2.0
@ -145,20 +148,7 @@ open class ToggleView: Control, Changeable {
// Update shadow layers frames to match the view's bounds
knobView.layer.insertSublayer(shadowLayer1, at: 0)
knobView.layer.insertSublayer(shadowLayer2, at: 0)
let shadowColor = VDSColor.paletteBlack.cgColor
shadowLayer1.cornerRadius = knobView.layer.cornerRadius
shadowLayer1.shadowColor = shadowColor
shadowLayer1.shadowOpacity = 0.24
shadowLayer1.shadowOffset = .init(width: 0, height: 1)
shadowLayer1.shadowRadius = 5.0
shadowLayer2.cornerRadius = knobView.layer.cornerRadius
shadowLayer2.shadowColor = shadowColor
shadowLayer2.shadowOpacity = 0.08
shadowLayer2.shadowOffset = .init(width: 0, height: 2)
shadowLayer2.shadowRadius = 2.0
}
/// Resets to default settings.
@ -225,7 +215,7 @@ open class ToggleView: Control, Changeable {
shadowLayer1.backgroundColor = knobColor.cgColor
shadowLayer2.backgroundColor = knobColor.cgColor
if disabled || !isAnimated {
if !isEnabled || !isAnimated {
toggleView.backgroundColor = toggleColor
knobView.backgroundColor = knobColor
constrainKnob()
@ -246,6 +236,19 @@ open class ToggleView: Control, Changeable {
shadowLayer1.frame = knobView.bounds
shadowLayer2.frame = knobView.bounds
let shadowColor = isEnabled ? VDSColor.paletteBlack.cgColor : VDSColor.paletteGray95.cgColor
shadowLayer1.cornerRadius = knobView.layer.cornerRadius
shadowLayer1.shadowColor = shadowColor
shadowLayer1.shadowOpacity = isEnabled ? 0.24 : 0.1
shadowLayer1.shadowOffset = .init(width: 0, height: 1)
shadowLayer1.shadowRadius = isEnabled ? 5.0 : 10.0
shadowLayer2.cornerRadius = knobView.layer.cornerRadius
shadowLayer2.shadowColor = shadowColor
shadowLayer2.shadowOpacity = isEnabled ? 0.08 : 0.04
shadowLayer2.shadowOffset = .init(width: 0, height: 2)
shadowLayer2.shadowRadius = 2.0
}
}

View File

@ -38,10 +38,10 @@ open class Tooltip: Control, TooltipLaunchable {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var imageView = UIImageView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.contentMode = .scaleAspectFill
$0.clipsToBounds = true
open var icon = Icon().with {
$0.name = .info
$0.size = .small
$0.isUserInteractionEnabled = false
}
open var closeButtonText: String = "Close" { didSet { setNeedsUpdate() }}
@ -111,18 +111,8 @@ open class Tooltip: Control, TooltipLaunchable {
open override func setup() {
super.setup()
if let image = BundleManager.shared.image(for: "info") {
infoImage = image
}
addSubview(imageView)
imageView.pinToSuperView()
heightConstraint = imageView.heightAnchor.constraint(equalToConstant: size.value.dimensions.height)
heightConstraint?.isActive = true
widthConstraint = imageView.widthAnchor.constraint(equalToConstant: size.value.dimensions.width)
widthConstraint?.isActive = true
addSubview(icon)
icon.pinToSuperView()
backgroundColor = .clear
isAccessibilityElement = true
@ -149,7 +139,6 @@ open class Tooltip: Control, TooltipLaunchable {
content = ""
fillColor = .primary
closeButtonText = "Close"
imageView.image = nil
shouldUpdateView = true
setNeedsUpdate()
}
@ -157,15 +146,12 @@ open class Tooltip: Control, TooltipLaunchable {
/// Function used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
//set the dimensions
let dimensions = size.value.dimensions
heightConstraint?.constant = dimensions.height
widthConstraint?.constant = dimensions.width
//get the size
icon.size = size.value
//get the color for the image
let imageColor = iconColorConfiguration.getColor(self)
imageView.image = infoImage.withTintColor(imageColor)
icon.color = iconColorConfiguration.getColor(self)
}
open override func updateAccessibility() {

View File

@ -71,8 +71,8 @@ open class TrailingTooltipLabel: View, TooltipLaunchable {
label.textPosition = labelTextPosition
label.attributes = labelAttributes
label.surface = surface
label.disabled = disabled
label.isEnabled = isEnabled
//add tooltip
if let labelText, !labelText.isEmpty {
label.addTooltip(model: .init(surface: surface, closeButtonText: tooltipCloseButtonText, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView))

View File

@ -10,466 +10,11 @@ import UIKit
import VDSFormControlsTokens
extension UIView {
public func constraint(with identifier: String) -> NSLayoutConstraint? {
return constraints.first { $0.identifier == identifier }
}
}
//--------------------------------------------------
// MARK: - Pinning
//--------------------------------------------------
extension UIView {
@discardableResult
/// Pins each to the all 4 anchor points to a view.
/// - Parameters:
/// - view: View that you will be pinned within.
/// - edges: Insets for each side.
/// - Returns: Yourself.
public func pin(_ view: UIView, with edges: UIEdgeInsets = UIEdgeInsets.zero) -> Self {
pinLeading(view.leadingAnchor, edges.left)
pinTrailing(view.trailingAnchor, edges.right)
pinTop(view.topAnchor, edges.top)
pinBottom(view.bottomAnchor, edges.bottom)
return self
}
@discardableResult
/// Pins each to the all 4 anchor points to the view you are set within.
/// - Parameter edges: Insets for each side.
/// - Returns: Yourself.
public func pinToSuperView(_ edges: UIEdgeInsets = UIEdgeInsets.zero) -> Self {
if let superview {
pin(superview, with: edges)
}
return self
}
}
//--------------------------------------------------
// MARK: - HeightAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
/// Adds a heightAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func height(_ constant: CGFloat) -> Self {
height(constant: constant)
return self
}
@discardableResult
/// Adds a heightAnchor where the height constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func heightGreaterThanEqualTo(_ constant: CGFloat) -> Self {
heightGreaterThanEqualTo(constant: constant)
return self
}
@discardableResult
/// Adds a heightAnchor where the height constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func heightLessThanEqualTo(_ constant: CGFloat) -> Self {
heightLessThanEqualTo(constant: constant)
return self
}
@discardableResult
/// Adds a heightAnchor for the constant passed into the method.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func height(constant: CGFloat) -> NSLayoutConstraint {
heightAnchor.constraint(equalToConstant: constant).activate()
}
@discardableResult
/// Adds a heightAnchor where the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func heightGreaterThanEqualTo(constant: CGFloat) -> NSLayoutConstraint {
heightAnchor.constraint(greaterThanOrEqualToConstant: constant).activate()
}
@discardableResult
/// Adds a heightAnchor where the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func heightLessThanEqualTo(constant: CGFloat) -> NSLayoutConstraint {
heightAnchor.constraint(lessThanOrEqualToConstant: constant).activate()
}
}
//--------------------------------------------------
// MARK: - WidthAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
/// Adds a widthAnchor.
/// - Parameter constant: Width Constant size.
/// - Returns: Yourself.
public func width(_ constant: CGFloat) -> Self {
width(constant: constant)
return self
}
@discardableResult
/// Adds a widthAnchor where the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func widthGreaterThanEqualTo(_ constant: CGFloat) -> Self {
widthGreaterThanEqualTo(constant: constant)
return self
}
@discardableResult
/// Adds a widthAnchor where the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func widthLessThanEqualTo(_ constant: CGFloat) -> Self {
widthLessThanEqualTo(constant: constant)
return self
}
@discardableResult
/// Adds a widthAnchor for the constant passed into the method.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func width(constant: CGFloat) -> NSLayoutConstraint {
widthAnchor.constraint(equalToConstant: constant).activate()
}
@discardableResult
/// Adds a widthAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func widthGreaterThanEqualTo(constant: CGFloat) -> NSLayoutConstraint {
widthAnchor.constraint(greaterThanOrEqualToConstant: constant).activate()
}
@discardableResult
/// Adds a widthAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func widthLessThanEqualTo(constant: CGFloat) -> NSLayoutConstraint {
widthAnchor.constraint(lessThanOrEqualToConstant: constant).activate()
}
}
//--------------------------------------------------
// MARK: - TopAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
/// Adds a topAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinTop(_ constant: CGFloat = 0.0) -> Self {
return pinTop(nil, constant)
}
@discardableResult
/// Adds a topAnchor to a specific YAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTop(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTop(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a topAnchor to a specific YAxisAnchor passed in using a lessThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTopLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTopLessThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a topAnchor to a specific YAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTopGreaterThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTopGreaterThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a topAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTop(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
guard let found else { return nil }
return topAnchor.constraint(equalTo: found, constant: constant).activate()
}
@discardableResult
/// Adds a topAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTopLessThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
guard let found else { return nil }
return topAnchor.constraint(lessThanOrEqualTo: found, constant: constant).activate()
}
@discardableResult
/// Adds a topAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTopGreaterThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
guard let found else { return nil }
return topAnchor.constraint(greaterThanOrEqualTo: found, constant: constant).activate()
}
}
//--------------------------------------------------
// MARK: - BottomAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
/// Adds a bottomAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinBottom(_ constant: CGFloat = 0.0) -> Self {
return pinBottom(nil, constant)
}
@discardableResult
/// Adds a bottomAnchor to a specific YAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinBottom(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinBottom(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a bottomAnchor to a specific YAxisAnchor passed in using a lessThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinBottomLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinBottomLessThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a bottomAnchor to a specific YAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinBottomGreaterThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinBottomGreaterThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a bottomAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinBottom(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
guard let found else { return nil }
return bottomAnchor.constraint(equalTo: found, constant: -constant).activate()
}
@discardableResult
/// Adds a bottomAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinBottomLessThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
guard let found else { return nil }
return bottomAnchor.constraint(lessThanOrEqualTo: found, constant: -constant).activate()
}
@discardableResult
/// Adds a bottomAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinBottomGreaterThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
guard let found else { return nil }
return bottomAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).activate()
}
}
//--------------------------------------------------
// MARK: - LeadingAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
/// Adds a leadingAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinLeading(_ constant: CGFloat = 0.0) -> Self {
return pinLeading(nil, constant)
}
@discardableResult
/// Adds a leadingAnchor to a specific XAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor.
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinLeading(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinLeading(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a leadingAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinLeadingLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinLeadingLessThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a leadingAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinLeadingGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinLeadingGreaterThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a leadingAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinLeading(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
guard let found else { return nil }
return leadingAnchor.constraint(equalTo: found, constant: constant).activate()
}
@discardableResult
/// Adds a leadingAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinLeadingLessThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
guard let found else { return nil }
return leadingAnchor.constraint(lessThanOrEqualTo: found, constant: constant).activate()
}
@discardableResult
/// Adds a leadingAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinLeadingGreaterThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
guard let found else { return nil }
return leadingAnchor.constraint(greaterThanOrEqualTo: found, constant: constant).activate()
}
}
//--------------------------------------------------
// MARK: - TrailingAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
/// Adds a trailingAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinTrailing(_ constant: CGFloat = 0.0) -> Self {
pinTrailing(nil, constant)
}
@discardableResult
/// Adds a trailingAnchor to a specific XAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor.
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTrailing(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTrailing(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a trailingAnchor to a specific XAxisAnchor passed in using a lessThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTrailingLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTrailingLessThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a trailingAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTrailingGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTrailingGreaterThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a trailingAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTrailing(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
guard let found else { return nil }
return trailingAnchor.constraint(equalTo: found, constant: -constant).activate()
}
@discardableResult
/// Adds a trailingAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTrailingLessThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
guard let found else { return nil }
return trailingAnchor.constraint(lessThanOrEqualTo: found, constant: -constant).activate()
}
@discardableResult
/// Adds a trailingAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTrailingGreaterThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
guard let found else { return nil }
return trailingAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).activate()
}
}
extension NSLayoutConstraint {
@discardableResult

View File

@ -7,8 +7,8 @@
import Foundation
/// Any object that can be disabled, which may change the appearance
public protocol Disabling {
/// Whether this object is disabled or not
var disabled: Bool { get set }
}
///// Any object that can be disabled, which may change the appearance
//public protocol Disabling {
// /// Whether this object is disabled or not
// var disabled: Bool { get set }
//}

View File

@ -0,0 +1,14 @@
//
// Enabling.swift
// VDS
//
// Created by Matt Bruce on 8/25/23.
//
import Foundation
/// Any object that can be Enabled, which may change the appearance
public protocol Enabling {
/// Whether this object is enabled or not
var isEnabled: Bool { get set }
}

View File

@ -0,0 +1,490 @@
//
// LayoutConstraintable.swift
// VDS
//
// Created by Matt Bruce on 8/22/23.
//
import Foundation
import UIKit
import VDSFormControlsTokens
public protocol LayoutConstraintable {
var superview: UIView? { get }
var leadingAnchor: NSLayoutXAxisAnchor { get }
var trailingAnchor: NSLayoutXAxisAnchor { get }
var topAnchor: NSLayoutYAxisAnchor { get }
var bottomAnchor: NSLayoutYAxisAnchor { get }
var widthAnchor: NSLayoutDimension { get }
var heightAnchor: NSLayoutDimension { get }
var centerXAnchor: NSLayoutXAxisAnchor { get }
var centerYAnchor: NSLayoutYAxisAnchor { get }
}
//--------------------------------------------------
// MARK: - Pinning
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Pins each to the all 4 anchor points to a view.
/// - Parameters:
/// - view: View that you will be pinned within.
/// - edges: Insets for each side.
/// - Returns: Yourself.
public func pin(_ view: UIView, with edges: UIEdgeInsets = UIEdgeInsets.zero) -> Self {
pinLeading(view.leadingAnchor, edges.left)
pinTrailing(view.trailingAnchor, edges.right)
pinTop(view.topAnchor, edges.top)
pinBottom(view.bottomAnchor, edges.bottom)
return self
}
@discardableResult
/// Pins each to the all 4 anchor points to the view you are set within.
/// - Parameter edges: Insets for each side.
/// - Returns: Yourself.
public func pinToSuperView(_ edges: UIEdgeInsets = UIEdgeInsets.zero) -> Self {
if let superview {
pin(superview, with: edges)
}
return self
}
}
//--------------------------------------------------
// MARK: - HeightAnchor
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Adds a heightAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func height(_ constant: CGFloat, _ priority: UILayoutPriority = .required) -> Self {
height(constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a heightAnchor where the height constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func heightGreaterThanEqualTo(_ constant: CGFloat, _ priority: UILayoutPriority = .required) -> Self {
heightGreaterThanEqualTo(constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a heightAnchor where the height constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func heightLessThanEqualTo(_ constant: CGFloat, _ priority: UILayoutPriority = .required) -> Self {
heightLessThanEqualTo(constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a heightAnchor for the constant passed into the method.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func height(constant: CGFloat, priority: UILayoutPriority = .required) -> NSLayoutConstraint {
heightAnchor.constraint(equalToConstant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a heightAnchor where the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func heightGreaterThanEqualTo(constant: CGFloat, priority: UILayoutPriority = .required) -> NSLayoutConstraint {
heightAnchor.constraint(greaterThanOrEqualToConstant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a heightAnchor where the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func heightLessThanEqualTo(constant: CGFloat, priority: UILayoutPriority = .required) -> NSLayoutConstraint {
heightAnchor.constraint(lessThanOrEqualToConstant: constant).with { $0.priority = priority; $0.isActive = true }
}
}
//--------------------------------------------------
// MARK: - WidthAnchor
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Adds a widthAnchor.
/// - Parameter constant: Width Constant size.
/// - Returns: Yourself.
public func width(_ constant: CGFloat, _ priority: UILayoutPriority = .required) -> Self {
width(constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a widthAnchor where the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func widthGreaterThanEqualTo(_ constant: CGFloat, _ priority: UILayoutPriority = .required) -> Self {
widthGreaterThanEqualTo(constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a widthAnchor where the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func widthLessThanEqualTo(_ constant: CGFloat, _ priority: UILayoutPriority = .required) -> Self {
widthLessThanEqualTo(constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a widthAnchor for the constant passed into the method.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func width(constant: CGFloat, priority: UILayoutPriority = .required) -> NSLayoutConstraint {
widthAnchor.constraint(equalToConstant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a widthAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func widthGreaterThanEqualTo(constant: CGFloat, priority: UILayoutPriority = .required) -> NSLayoutConstraint {
widthAnchor.constraint(greaterThanOrEqualToConstant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a widthAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter constant: Constant size.
/// - Returns: The Constraint that was created.
public func widthLessThanEqualTo(constant: CGFloat, priority: UILayoutPriority = .required) -> NSLayoutConstraint {
widthAnchor.constraint(lessThanOrEqualToConstant: constant).with { $0.priority = priority; $0.isActive = true }
}
}
//--------------------------------------------------
// MARK: - TopAnchor
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Adds a topAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinTop(_ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
return pinTop(nil, constant, priority)
}
@discardableResult
/// Adds a topAnchor to a specific YAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTop(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinTop(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a topAnchor to a specific YAxisAnchor passed in using a lessThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTopLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinTopLessThanOrEqualTo(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a topAnchor to a specific YAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTopGreaterThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinTopGreaterThanOrEqualTo(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a topAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTop(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
guard let found else { return nil }
return topAnchor.constraint(equalTo: found, constant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a topAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTopLessThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
guard let found else { return nil }
return topAnchor.constraint(lessThanOrEqualTo: found, constant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a topAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the topAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTopGreaterThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
guard let found else { return nil }
return topAnchor.constraint(greaterThanOrEqualTo: found, constant: constant).with { $0.priority = priority; $0.isActive = true }
}
}
//--------------------------------------------------
// MARK: - BottomAnchor
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Adds a bottomAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinBottom(_ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
return pinBottom(nil, constant, priority)
}
@discardableResult
/// Adds a bottomAnchor to a specific YAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinBottom(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinBottom(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a bottomAnchor to a specific YAxisAnchor passed in using a lessThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinBottomLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinBottomLessThanOrEqualTo(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a bottomAnchor to a specific YAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinBottomGreaterThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinBottomGreaterThanOrEqualTo(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a bottomAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinBottom(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
guard let found else { return nil }
return bottomAnchor.constraint(equalTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a bottomAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinBottomLessThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
guard let found else { return nil }
return bottomAnchor.constraint(lessThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a bottomAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the bottomAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinBottomGreaterThanOrEqualTo(anchor: NSLayoutYAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
guard let found else { return nil }
return bottomAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true }
}
}
//--------------------------------------------------
// MARK: - LeadingAnchor
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Adds a leadingAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinLeading(_ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
return pinLeading(nil, constant, priority)
}
@discardableResult
/// Adds a leadingAnchor to a specific XAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor.
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinLeading(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinLeading(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a leadingAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinLeadingLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinLeadingLessThanOrEqualTo(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a leadingAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinLeadingGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinLeadingGreaterThanOrEqualTo(anchor: anchor, constant: constant, priority: priority)
return self
}
@discardableResult
/// Adds a leadingAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinLeading(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
guard let found else { return nil }
return leadingAnchor.constraint(equalTo: found, constant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a leadingAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinLeadingLessThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
guard let found else { return nil }
return leadingAnchor.constraint(lessThanOrEqualTo: found, constant: constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a leadingAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the leadingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinLeadingGreaterThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
guard let found else { return nil }
return leadingAnchor.constraint(greaterThanOrEqualTo: found, constant: constant).with { $0.priority = priority; $0.isActive = true }
}
}
//--------------------------------------------------
// MARK: - TrailingAnchor
//--------------------------------------------------
extension LayoutConstraintable {
@discardableResult
/// Adds a trailingAnchor.
/// - Parameter constant: Constant size.
/// - Returns: Yourself.
public func pinTrailing(_ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinTrailing(nil, constant)
}
@discardableResult
/// Adds a trailingAnchor to a specific XAxisAnchor.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor.
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTrailing(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinTrailing(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a trailingAnchor to a specific XAxisAnchor passed in using a lessThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTrailingLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ priority: UILayoutPriority = .required) -> Self {
pinTrailingLessThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a trailingAnchor to a specific XAxisAnchor passed in using a greaterThanOrEqualTo Constraint
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: Yourself.
public func pinTrailingGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
pinTrailingGreaterThanOrEqualTo(anchor: anchor, constant: constant)
return self
}
@discardableResult
/// Adds a trailingAnchor for the constant passed into the method.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTrailing(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
guard let found else { return nil }
return trailingAnchor.constraint(equalTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a trailingAnchor with the constant passed in using a lessThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTrailingLessThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
guard let found else { return nil }
return trailingAnchor.constraint(lessThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true }
}
@discardableResult
/// Adds a trailingAnchor with the constant passed in using a greaterThanOrEqualTo Constraint.
/// - Parameter anchor:The anchor in which to attach the trailingAnchor
/// - constant: Constant size.
/// - Returns: The Constraint that was created.
public func pinTrailingGreaterThanOrEqualTo(anchor: NSLayoutXAxisAnchor?, constant: CGFloat = 0.0, priority: UILayoutPriority = .required) -> NSLayoutConstraint? {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
guard let found else { return nil }
return trailingAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true }
}
}
//--------------------------------------------------
// MARK: - Implementations
//--------------------------------------------------
extension UIView: LayoutConstraintable {}
extension UILayoutGuide: LayoutConstraintable {
public var superview: UIView? {
owningView
}
}

View File

@ -9,7 +9,7 @@ import Foundation
import UIKit
import Combine
public protocol ViewProtocol: AnyObject, Initable, Resettable, Disabling, Surfaceable {
public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surfaceable {
/// Set of Subscribers for any Publishers for this Control.
var subscribers: Set<AnyCancellable> { get set }

View File

@ -1,3 +1,9 @@
1.0.40
=======
- Refactored a bit of code to remove "disabled" and move to "isEnabled" Apple Standard.
- Fixed Layout Constraint issues
- Added fixes for Accessibility
1.0.39
=======
- CXTDT-423141 - Tabs - Selected Tab dark mode text color