Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into feature/textLink

This commit is contained in:
Matt Bruce 2022-11-15 10:39:33 -06:00
commit a5cd93b71c
19 changed files with 657 additions and 263 deletions

View File

@ -44,6 +44,7 @@
EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200528B526D6006B9984 /* CheckboxGroup.swift */; };
EA89201328B568D8006B9984 /* RadioBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201228B568D8006B9984 /* RadioBox.swift */; };
EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */; };
EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA978EC4291D6AFE00ACC883 /* AnyLabelAttribute.swift */; };
EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */; };
EAA5EEB728ECC03A003B3210 /* ToolTipLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */; };
EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EAA5EEB828ECD24B003B3210 /* Icons.xcassets */; };
@ -130,6 +131,7 @@
EA89200528B526D6006B9984 /* CheckboxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroup.swift; sourceTree = "<group>"; };
EA89201228B568D8006B9984 /* RadioBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBox.swift; sourceTree = "<group>"; };
EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxGroup.swift; sourceTree = "<group>"; };
EA978EC4291D6AFE00ACC883 /* AnyLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyLabelAttribute.swift; sourceTree = "<group>"; };
EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLabelAttribute.swift; sourceTree = "<group>"; };
EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolTipLabelAttribute.swift; sourceTree = "<group>"; };
EAA5EEB828ECD24B003B3210 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = "<group>"; };
@ -480,6 +482,7 @@
children = (
EAF7F0A3289B017C00B287F5 /* LabelAttributeModel.swift */,
EAF7F0B2289B1ADC00B287F5 /* ActionLabelAttribute.swift */,
EA978EC4291D6AFE00ACC883 /* AnyLabelAttribute.swift */,
EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */,
EAF7F0B0289B177F00B287F5 /* ColorLabelAttribute.swift */,
EAF7F0AA289B13FD00B287F5 /* FontLabelAttribute.swift */,
@ -631,6 +634,7 @@
EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */,
EAC925842911C63100091998 /* Colorable.swift in Sources */,
EA3361C5289030FC0071C351 /* Accessable.swift in Sources */,
EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */,
EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */,
EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */,
EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */,

View File

@ -86,7 +86,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable {
open func reset() {
backgroundColor = .clear
surface = .light
disabled = false
disabled = false
}
// MARK: - ViewProtocol
@ -94,5 +94,5 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable {
open func setup() {
translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false
}
}
}

View File

@ -43,6 +43,11 @@ public class SelectorGroupHandlerBase<HandlerType: Control>: Control {
self?.sendActions(for: .valueChanged)
}
}
public override func reset() {
super.reset()
selectorViews.forEach{ $0.reset() }
}
}
public class SelectorGroupSelectedHandlerBase<HandlerType: Control>: SelectorGroupHandlerBase<HandlerType>{

View File

@ -18,7 +18,13 @@ public enum BadgeFillColor: String, Codable, CaseIterable {
@objc(VDSBadge)
public class Badge: View, Accessable {
private var label = Label()
private var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.adjustsFontSizeToFitWidth = false
$0.lineBreakMode = .byTruncatingTail
$0.textPosition = .left
$0.typograpicalStyle = .BoldBodySmall
}
//--------------------------------------------------
// MARK: - Public Properties
@ -62,8 +68,6 @@ public class Badge: View, Accessable {
addSubview(label)
layer.cornerRadius = 2
label.adjustsFontSizeToFitWidth = false
label.lineBreakMode = .byTruncatingTail
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4).isActive = true
label.topAnchor.constraint(equalTo: topAnchor, constant: 2).isActive = true
@ -73,10 +77,27 @@ public class Badge: View, Accessable {
maxWidthConstraint = label.widthAnchor.constraint(lessThanOrEqualToConstant: 100)
minWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 23)
minWidthConstraint?.isActive = true
}
public override func reset() {
super.reset()
label.reset()
label.lineBreakMode = .byTruncatingTail
label.textPosition = .left
label.typograpicalStyle = .BoldBodySmall
fillColor = .red
text = ""
maxWidth = nil
numberOfLines = 1
accessibilityHintEnabled = nil
accessibilityHintDisabled = nil
accessibilityValueEnabled = nil
accessibilityValueDisabled = nil
accessibilityLabelEnabled = nil
accessibilityLabelDisabled = nil
setAccessibilityLabel()
}
@ -160,10 +181,9 @@ public class Badge: View, Accessable {
//--------------------------------------------------
open override func updateView() {
backgroundColor = backgroundColor()
label.textColorConfiguration = textColorConfiguration()
label.numberOfLines = numberOfLines
label.textPosition = .left
label.typograpicalStyle = .BoldBodySmall
label.text = text
label.surface = surface
label.disabled = disabled

View File

@ -83,7 +83,10 @@ open class TextLink: Control {
open override func reset() {
super.reset()
label.reset()
size = .large
text = nil
accessibilityCustomActions = []
accessibilityTraits = .staticText
}

View File

@ -96,6 +96,12 @@ open class TextLinkCaret: Control {
open override func reset() {
super.reset()
label.reset()
label.typograpicalStyle = TypographicalStyle.BoldBodyLarge
text = nil
iconPosition = .right
accessibilityCustomActions = []
accessibilityTraits = .staticText
}

View File

@ -51,47 +51,51 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er
}
private var shouldShowLabels: Bool {
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
guard labelText?.isEmpty == false || childText?.isEmpty == false || labelAttributedText?.string.isEmpty == false || childAttributedText?.string.isEmpty == false else { return false }
return true
}
private var mainStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .vertical
}
}()
private var mainStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .vertical
}
private var selectorStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
}
}()
private var selectorStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
}
private var selectorLabelStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
}
}()
private var selectorLabelStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
}
private var primaryLabel = Label()
private var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BoldBodyLarge
}
private var secondaryLabel = Label()
private var childLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodyLarge
}
private var errorLabel = Label()
private var errorLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodyMedium
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var selectorView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
}()
public var selectorView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
//can't bind to @Proxy
open override var isSelected: Bool { didSet { didChange() }}
@ -99,11 +103,27 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er
open var labelText: String? { didSet { didChange() }}
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var labelAttributedText: NSAttributedString? {
didSet {
label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true)
label.attributedText = labelAttributedText
didChange()
}
}
open var childText: String? { didSet { didChange() }}
open var childTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var childAttributedText: NSAttributedString? {
didSet {
childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true)
childLabel.attributedText = childAttributedText
didChange()
}
}
open var showError: Bool = false { didSet { didChange() }}
open var errorText: String? { didSet { didChange() }}
@ -158,8 +178,8 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er
mainStackView.addArrangedSubview(errorLabel)
selectorStackView.addArrangedSubview(selectorView)
selectorStackView.addArrangedSubview(selectorLabelStackView)
selectorLabelStackView.addArrangedSubview(primaryLabel)
selectorLabelStackView.addArrangedSubview(secondaryLabel)
selectorLabelStackView.addArrangedSubview(label)
selectorLabelStackView.addArrangedSubview(childLabel)
let selectorSize = getSelectorSize()
selectorHeightConstraint = selectorView.heightAnchor.constraint(equalToConstant: selectorSize.height)
@ -184,28 +204,31 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er
//add the stackview to hold the 2 labels
//top label
if let labelText {
primaryLabel.textPosition = .left
primaryLabel.typograpicalStyle = .BoldBodyLarge
primaryLabel.text = labelText
primaryLabel.surface = surface
primaryLabel.disabled = disabled
primaryLabel.attributes = labelTextAttributes
primaryLabel.isHidden = false
label.surface = surface
label.disabled = disabled
label.attributes = labelTextAttributes
label.text = labelText
label.isHidden = false
} else if labelAttributedText != nil {
label.isHidden = false
} else {
primaryLabel.isHidden = true
label.isHidden = true
}
//bottom label
if let childText {
secondaryLabel.textPosition = .left
secondaryLabel.typograpicalStyle = .BodyLarge
secondaryLabel.text = childText
secondaryLabel.surface = surface
secondaryLabel.disabled = disabled
secondaryLabel.attributes = childTextAttributes
secondaryLabel.isHidden = false
childLabel.text = childText
childLabel.surface = surface
childLabel.disabled = disabled
childLabel.attributes = childTextAttributes
childLabel.isHidden = false
} else if childAttributedText != nil {
childLabel.isHidden = false
} else {
secondaryLabel.isHidden = true
childLabel.isHidden = true
}
selectorStackView.spacing = 12
selectorLabelStackView.spacing = 4
@ -219,8 +242,6 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er
//either add/remove the error from the main stack
if let errorText, shouldShowError {
errorLabel.textPosition = .left
errorLabel.typograpicalStyle = .BodyMedium
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
@ -232,8 +253,37 @@ open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Er
}
}
public override func reset() {
open override func reset() {
super.reset()
label.reset()
childLabel.reset()
errorLabel.reset()
label.typograpicalStyle = .BoldBodyLarge
childLabel.typograpicalStyle = .BodyLarge
errorLabel.typograpicalStyle = .BodyMedium
labelText = nil
labelTextAttributes = nil
labelAttributedText = nil
childText = nil
childTextAttributes = nil
childAttributedText = nil
showError = false
errorText = nil
inputId = nil
value = nil
dataAnalyticsTrack = nil
dataClickStream = nil
dataTrack = nil
accessibilityHintEnabled = nil
accessibilityHintDisabled = nil
accessibilityValueEnabled = nil
accessibilityValueDisabled = nil
accessibilityLabelEnabled = nil
accessibilityLabelDisabled = nil
isSelected = false
updateSelector()
setAccessibilityLabel()
}

View File

@ -0,0 +1,36 @@
//
// AnyAttribute.swift
// VDS
//
// Created by Matt Bruce on 11/10/22.
//
import Foundation
public struct AnyAttribute: LabelAttributeModel {
public var id = UUID()
public var location: Int
public var length: Int
public var key: NSAttributedString.Key
public var value: AnyHashable
public init(location: Int, length: Int, key: NSAttributedString.Key, value: AnyHashable) {
self.location = location
self.length = length
self.key = key
self.value = value
}
public func isEqual(_ equatable: AnyAttribute) -> Bool {
return id == equatable.id && range == equatable.range && key == equatable.key && value == equatable.value
}
public static func == (lhs: AnyAttribute, rhs: AnyAttribute) -> Bool {
lhs.isEqual(rhs)
}
public func setAttribute(on attributedString: NSMutableAttributedString) {
attributedString.removeAttribute(key, range: range)
attributedString.addAttribute(key, value: value, range: range)
}
}

View File

@ -19,17 +19,20 @@ public struct ColorLabelAttribute: LabelAttributeModel {
public var location: Int
public var length: Int
public var color: UIColor
public var isForegroundColor: Bool
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(location: Int, length: Int, color: UIColor = .black) {
public init(location: Int, length: Int, color: UIColor = .black, isForegroundColor: Bool = true) {
self.location = location
self.length = length
self.color = color
self.isForegroundColor = isForegroundColor
}
public func setAttribute(on attributedString: NSMutableAttributedString) {
attributedString.removeAttribute(.foregroundColor, range: range)
attributedString.addAttribute(.foregroundColor, value: color, range: range)
let attributeKey = isForegroundColor ? NSAttributedString.Key.foregroundColor : NSAttributedString.Key.backgroundColor
attributedString.removeAttribute(attributeKey, range: range)
attributedString.addAttribute(attributeKey, value: color, range: range)
}
}

View File

@ -24,21 +24,59 @@ public struct FontLabelAttribute: LabelAttributeModel {
public var length: Int
public var style: TypographicalStyle
public var color: UIColor
public var textPosition: TextPosition
public var lineBreakMode: NSLineBreakMode
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(location: Int, length: Int, style: TypographicalStyle, color: UIColor = .black) {
public init(location: Int, length: Int, style: TypographicalStyle, color: UIColor = .black, textPosition: TextPosition = .left, lineBreakMode: NSLineBreakMode = .byWordWrapping) {
self.location = location
self.length = length
self.style = style
self.color = color
self.textPosition = textPosition
self.lineBreakMode = lineBreakMode
}
public func setAttribute(on attributedString: NSMutableAttributedString) {
attributedString.removeAttribute(.font, range: range)
attributedString.removeAttribute(.foregroundColor, range: range)
attributedString.addAttribute(.font, value: style.font, range: range)
attributedString.addAttribute(.foregroundColor, value: color, range: range)
setStyleAttributes(attributedString)
}
private func setStyleAttributes(_ attributedString: NSMutableAttributedString) {
//set letterSpacing
if style.letterSpacing > 0.0 {
attributedString.removeAttribute(.kern, range: range)
attributedString.addAttribute(.kern, value: style.letterSpacing, range: range)
}
//set lineHeight
if style.lineHeight > 0.0 {
let lineHeight = style.lineHeight
let adjustment = lineHeight > style.font.lineHeight ? 2.0 : 1.0
let baselineOffset = (lineHeight - style.font.lineHeight) / 2.0 / adjustment
let paragraph = NSMutableParagraphStyle().with {
$0.maximumLineHeight = lineHeight
$0.minimumLineHeight = lineHeight
$0.alignment = textPosition.textAlignment
$0.lineBreakMode = lineBreakMode
}
attributedString.removeAttribute(.baselineOffset, range: range)
attributedString.removeAttribute(.paragraphStyle, range: range)
attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: range)
attributedString.addAttribute(.paragraphStyle, value: paragraph, range: range)
} else if textPosition != .left {
let paragraph = NSMutableParagraphStyle().with {
$0.alignment = textPosition.textAlignment
$0.lineBreakMode = lineBreakMode
}
attributedString.removeAttribute(.paragraphStyle, range: range)
attributedString.addAttribute(.paragraphStyle, value: paragraph, range: range)
}
}
}

View File

@ -22,5 +22,28 @@ extension LabelAttributeModel {
public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool {
lhs.isEqual(rhs)
}
}
public extension NSAttributedString {
func createAttributeModels() -> [(any LabelAttributeModel)] {
var attributes: [any VDS.LabelAttributeModel] = []
enumerateAttributes(in: NSMakeRange(0, length)) { attributeMap, range, stop in
attributeMap.forEach { (key: NSAttributedString.Key, value: Any) in
if let attribute = NSAttributedString.createAttributeModelFor(key: key, range: range, value: value) {
attributes.append(attribute)
}
}
}
return attributes
}
static func createAttributeModelFor(key: NSAttributedString.Key, range: NSRange, value: Any) -> (any LabelAttributeModel)? {
guard let value = value as? AnyHashable else { return nil }
guard let font = value as? UIFont, let style = TypographicalStyle.style(for: font.fontName, size: font.pointSize), key == .font
else {
return AnyAttribute(location: range.location, length: range.length, key: key, value: value)
}
return FontLabelAttribute(location: range.location, length: range.length, style: style)
}
}

View File

@ -27,6 +27,8 @@ open class LabelBase: UILabel, Handlerable, ViewProtocol, Resettable {
// MARK: - Properties
//--------------------------------------------------
private var initialSetupPerformed = false
open var useAttributedText: Bool = false
open var surface: Surface = .light { didSet { didChange() }}
@ -119,49 +121,53 @@ open class LabelBase: UILabel, Handlerable, ViewProtocol, Resettable {
// MARK: - Overrides
//--------------------------------------------------
open func updateView() {
textAlignment = textPosition.textAlignment
textColor = textColorConfiguration.getColor(self)
font = typograpicalStyle.font
if !useAttributedText {
textAlignment = textPosition.textAlignment
textColor = textColorConfiguration.getColor(self)
font = typograpicalStyle.font
if let text = text, let font = font, let textColor = textColor {
//clear the arrays holding actions
accessibilityCustomActions = []
//create the primary string
let startingAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: textColor]
let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes)
//set the local lineHeight/lineSpacing attributes
setStyleAttributes(attributedString: mutableText)
applyAttributes(mutableText)
//set the attributed text
attributedText = mutableText
}
}
}
if let text = text, let font = font, let textColor = textColor {
//clear the arrays holding actions
accessibilityCustomActions = []
actions = []
//create the primary string
let startingAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: textColor]
let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes)
//set the local lineHeight/lineSpacing attributes
setStyleAttributes(attributedString: mutableText)
if let attributes = attributes {
//loop through the models attributes
for attribute in attributes {
// MARK: - Private Attributes
private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) {
actions = []
if let attributes = attributes {
//loop through the models attributes
for attribute in attributes {
//add attribute on the string
attribute.setAttribute(on: mutableAttributedString)
//see if the attribute is Actionable
if let actionable = attribute as? any ActionLabelAttributeModel{
//create a accessibleAction
let customAccessibilityAction = customAccessibilityAction(range: actionable.range, accessibleText: actionable.accessibleText)
//add attribute on the string
attribute.setAttribute(on: mutableText)
//see if the attribute is Actionable
if let actionable = attribute as? any ActionLabelAttributeModel{
//create a accessibleAction
let customAccessibilityAction = customAccessibilityAction(range: actionable.range, accessibleText: actionable.accessibleText)
//create a wrapper for the attributes range, block and
actions.append(LabelAction(range: actionable.range, action: actionable.action, accessibilityID: customAccessibilityAction?.hashValue ?? -1))
}
//create a wrapper for the attributes range, block and
actions.append(LabelAction(range: actionable.range, action: actionable.action, accessibilityID: customAccessibilityAction?.hashValue ?? -1))
}
}
//only enabled if enabled and has actions
isUserInteractionEnabled = !disabled && !actions.isEmpty
//set the attributed text
attributedText = mutableText
}
}
// MARK: - Private Attributes
private func setStyleAttributes(attributedString: NSMutableAttributedString) {
//get the range
let entireRange = NSRange(location: 0, length: attributedString.length)

View File

@ -46,57 +46,88 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var mainStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .vertical
$0.spacing = 0
}
}()
private var selectorStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
}
}()
private var selectorLeftLabelStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
}
}()
private var textLabel = Label()
private var subTextLabel = Label()
private var mainStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .vertical
$0.spacing = 0
}
private var subTextRightLabel = Label()
private var selectorStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
$0.spacing = 12
}
private var selectorLeftLabelStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
$0.spacing = 4
$0.isHidden = false
}
private var textLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BoldBodyLarge
}
private var subTextLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodyLarge
}
private var subTextRightLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .right
$0.typograpicalStyle = .BodyLarge
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var selectorView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
}()
public var selectorView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
open var text: String = "Default Text" { didSet { didChange() }}
open var textAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var textAttributedText: NSAttributedString? {
didSet {
textLabel.useAttributedText = !(textAttributedText?.string.isEmpty ?? true)
textLabel.attributedText = textAttributedText
didChange()
}
}
open var subText: String? { didSet { didChange() }}
open var subTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var subTextAttributedText: NSAttributedString? {
didSet {
subTextLabel.useAttributedText = !(subTextAttributedText?.string.isEmpty ?? true)
subTextLabel.attributedText = subTextAttributedText
didChange()
}
}
open var subTextRight: String? { didSet { didChange() }}
open var subTextRightAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var subTextRightAttributedText: NSAttributedString? {
didSet {
subTextRightLabel.useAttributedText = !(subTextRightAttributedText?.string.isEmpty ?? true)
subTextRightLabel.attributedText = subTextRightAttributedText
didChange()
}
}
open var strikethrough: Bool = false { didSet { didChange() }}
open var inputId: String? { didSet { didChange() }}
@ -147,10 +178,6 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{
selectorLeftLabelStackView.addArrangedSubview(textLabel)
selectorLeftLabelStackView.addArrangedSubview(subTextLabel)
selectorStackView.spacing = 12
selectorLeftLabelStackView.spacing = 4
selectorLeftLabelStackView.isHidden = false
updateSelector()
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
@ -162,15 +189,12 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{
mainStackView.leadingAnchor.constraint(equalTo: selectorView.leadingAnchor, constant: 16).isActive = true
mainStackView.trailingAnchor.constraint(equalTo: selectorView.trailingAnchor, constant: -16).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: selectorView.bottomAnchor, constant: -16).isActive = true
}
func updateLabels() {
//add the stackview to hold the 2 labels
//text label
textLabel.textPosition = .left
textLabel.typograpicalStyle = .BoldBodyLarge
textLabel.text = text
textLabel.surface = surface
textLabel.disabled = disabled
@ -178,26 +202,30 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{
//subText label
if let subText {
subTextLabel.textPosition = .left
subTextLabel.typograpicalStyle = .BodyLarge
subTextLabel.text = subText
subTextLabel.surface = surface
subTextLabel.disabled = disabled
subTextLabel.attributes = subTextAttributes
subTextLabel.isHidden = false
} else if subTextAttributedText != nil {
subTextLabel.isHidden = false
} else {
subTextLabel.isHidden = true
}
//subTextRight label
if let subTextRight {
subTextRightLabel.textPosition = .right
subTextRightLabel.typograpicalStyle = .BodyLarge
subTextRightLabel.text = subTextRight
subTextRightLabel.surface = surface
subTextRightLabel.disabled = disabled
subTextRightLabel.attributes = subTextRightAttributes
subTextRightLabel.isHidden = false
} else if subTextAttributedText != nil {
subTextRightLabel.isHidden = false
} else {
subTextRightLabel.isHidden = true
}
@ -205,6 +233,38 @@ open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{
public override func reset() {
super.reset()
textLabel.reset()
subTextLabel.reset()
subTextRightLabel.reset()
textLabel.typograpicalStyle = .BoldBodyLarge
subTextLabel.typograpicalStyle = .BodyLarge
subTextRightLabel.typograpicalStyle = .BodyLarge
text = "Default Text"
textAttributes = nil
textAttributedText = nil
subText = nil
subTextAttributes = nil
subTextAttributedText = nil
subTextRight = nil
subTextRightAttributes = nil
subTextRightAttributedText = nil
strikethrough = false
inputId = nil
value = nil
dataAnalyticsTrack = nil
dataClickStream = nil
dataTrack = nil
accessibilityHintEnabled = nil
accessibilityHintDisabled = nil
accessibilityValueEnabled = nil
accessibilityValueDisabled = nil
accessibilityLabelEnabled = nil
accessibilityLabelDisabled = nil
isSelected = false
updateSelector()
setAccessibilityLabel()
}

View File

@ -49,7 +49,7 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
@ -59,70 +59,90 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
}
private var shouldShowLabels: Bool {
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
guard labelText?.isEmpty == false || childText?.isEmpty == false || labelAttributedText?.string.isEmpty == false || childAttributedText?.string.isEmpty == false else { return false }
return true
}
private var mainStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .vertical
}
}()
private var selectorStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
}
}()
private var mainStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .vertical
}
private var selectorLabelStackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
}
}()
private var selectorStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alignment = .top
$0.axis = .horizontal
}
private var primaryLabel = Label()
private var selectorLabelStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
}
private var secondaryLabel = Label()
private var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BoldBodyLarge
}
private var childLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodyLarge
}
private var errorLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodyMedium
}
private var errorLabel = Label()
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var selectorView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
}()
public var selectorView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
open var labelText: String? { didSet { didChange() }}
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var labelAttributedText: NSAttributedString? {
didSet {
label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true)
label.attributedText = labelAttributedText
didChange()
}
}
open var childText: String? { didSet { didChange() }}
open var childTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }}
open var childAttributedText: NSAttributedString? {
didSet {
childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true)
childLabel.attributedText = childAttributedText
didChange()
}
}
open var showError: Bool = false { didSet { didChange() }}
open var errorText: String? { didSet { didChange() }}
open var inputId: String? { didSet { didChange() }}
open var value: AnyHashable? { didSet { didChange() }}
open var dataAnalyticsTrack: String? { didSet { didChange() }}
open var dataClickStream: String? { didSet { didChange() }}
open var dataTrack: String? { didSet { didChange() }}
open var accessibilityHintEnabled: String? { didSet { didChange() }}
open var accessibilityHintDisabled: String? { didSet { didChange() }}
@ -134,7 +154,7 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
open var accessibilityLabelEnabled: String? { didSet { didChange() }}
open var accessibilityLabelDisabled: String? { didSet { didChange() }}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
@ -146,7 +166,7 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open override func setup() {
super.setup()
@ -163,54 +183,58 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
mainStackView.addArrangedSubview(errorLabel)
selectorStackView.addArrangedSubview(selectorView)
selectorStackView.addArrangedSubview(selectorLabelStackView)
selectorLabelStackView.addArrangedSubview(primaryLabel)
selectorLabelStackView.addArrangedSubview(secondaryLabel)
selectorLabelStackView.addArrangedSubview(label)
selectorLabelStackView.addArrangedSubview(childLabel)
let selectorSize = getSelectorSize()
selectorHeightConstraint = selectorView.heightAnchor.constraint(equalToConstant: selectorSize.height)
selectorHeightConstraint?.isActive = true
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
selectorWidthConstraint?.isActive = true
updateSelector()
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
func updateLabels() {
//deal with labels
if shouldShowLabels {
//add the stackview to hold the 2 labels
//top label
if let labelText {
primaryLabel.textPosition = .left
primaryLabel.typograpicalStyle = .BoldBodyLarge
primaryLabel.text = labelText
primaryLabel.surface = surface
primaryLabel.disabled = disabled
primaryLabel.attributes = labelTextAttributes
primaryLabel.isHidden = false
label.text = labelText
label.surface = surface
label.disabled = disabled
label.attributes = labelTextAttributes
label.isHidden = false
} else if labelAttributedText != nil {
label.isHidden = false
} else {
primaryLabel.isHidden = true
label.isHidden = true
}
//bottom label
if let childText {
secondaryLabel.textPosition = .left
secondaryLabel.typograpicalStyle = .BodyLarge
secondaryLabel.text = childText
secondaryLabel.surface = surface
secondaryLabel.disabled = disabled
secondaryLabel.attributes = childTextAttributes
secondaryLabel.isHidden = false
childLabel.text = childText
childLabel.surface = surface
childLabel.disabled = disabled
childLabel.attributes = childTextAttributes
childLabel.isHidden = false
} else if childAttributedText != nil {
childLabel.isHidden = false
} else {
secondaryLabel.isHidden = true
childLabel.isHidden = true
}
selectorStackView.spacing = 12
selectorLabelStackView.spacing = 4
@ -224,8 +248,6 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
//either add/remove the error from the main stack
if let errorText, shouldShowError {
errorLabel.textPosition = .left
errorLabel.typograpicalStyle = .BodyMedium
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
@ -240,14 +262,44 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
public override func reset() {
super.reset()
label.reset()
childLabel.reset()
errorLabel.reset()
label.typograpicalStyle = .BoldBodyLarge
childLabel.typograpicalStyle = .BodyLarge
errorLabel.typograpicalStyle = .BodyMedium
labelText = nil
labelTextAttributes = nil
labelAttributedText = nil
childText = nil
childTextAttributes = nil
childAttributedText = nil
showError = false
errorText = nil
inputId = nil
value = nil
dataAnalyticsTrack = nil
dataClickStream = nil
dataTrack = nil
accessibilityHintEnabled = nil
accessibilityHintDisabled = nil
accessibilityValueEnabled = nil
accessibilityValueDisabled = nil
accessibilityLabelEnabled = nil
accessibilityLabelDisabled = nil
isSelected = false
updateSelector()
setAccessibilityLabel()
}
/// This will checkbox the state of the Selector and execute the actionBlock if provided.
open func toggle() {
guard !isSelected else { return }
//removed error
if showError && isSelected == false {
showError.toggle()
@ -255,10 +307,10 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
isSelected.toggle()
sendActions(for: .valueChanged)
}
//--------------------------------------------------
// MARK: - State
//--------------------------------------------------
//--------------------------------------------------
open override func updateView() {
updateLabels()
updateSelector()
@ -323,7 +375,7 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
let bounds = selectorView.bounds
let length = max(bounds.size.height, bounds.size.width)
guard length > 0.0, shapeLayer == nil else { return }
//get the colors
let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self)
let borderColor = radioButtonBorderColorConfiguration.getColor(self)
@ -337,9 +389,9 @@ open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable,
if shapeLayer == nil {
let selectedBounds = radioButtonSelectedSize
let bezierPath = UIBezierPath(ovalIn: CGRect(x: (bounds.width - selectedBounds.width) / 2,
y: (bounds.height - selectedBounds.height) / 2,
y: (bounds.height - selectedBounds.height) / 2,
width: radioButtonSelectedSize.width,
height: radioButtonSelectedSize.height))
height: radioButtonSelectedSize.height))
let shapeLayer = CAShapeLayer()
self.shapeLayer = shapeLayer
shapeLayer.frame = bounds

View File

@ -43,18 +43,14 @@ open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var selectorView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
}()
public var fillView: UIImageView = {
return UIImageView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.contentMode = .scaleAspectFit
}
}()
public var selectorView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
public var fillView = UIImageView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.contentMode = .scaleAspectFit
}
open var fillImage: UIImage? { didSet { didChange() }}
@ -126,6 +122,24 @@ open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable
public override func reset() {
super.reset()
fillImage = nil
text = ""
primaryColor = nil
secondaryColor = nil
strikethrough = false
inputId = nil
value = nil
dataAnalyticsTrack = nil
dataClickStream = nil
dataTrack = nil
accessibilityHintEnabled = nil
accessibilityHintDisabled = nil
accessibilityValueEnabled = nil
accessibilityValueDisabled = nil
accessibilityLabelEnabled = nil
accessibilityLabelDisabled = nil
setNeedsDisplay()
setAccessibilityLabel()
}

View File

@ -46,14 +46,20 @@ open class EntryField: Control, Accessable {
internal var titleLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.attributes = []
$0.textPosition = .left
$0.typograpicalStyle = .BodySmall
}
internal var errorLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodySmall
}
internal var helperLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodySmall
}
internal var containerView: UIView = {
@ -256,6 +262,38 @@ open class EntryField: Control, Accessable {
public override func reset() {
super.reset()
titleLabel.reset()
errorLabel.reset()
helperLabel.reset()
titleLabel.textPosition = .left
titleLabel.typograpicalStyle = .BodySmall
errorLabel.textPosition = .left
errorLabel.typograpicalStyle = .BodySmall
helperLabel.textPosition = .left
helperLabel.typograpicalStyle = .BodySmall
labelText = nil
helperText = nil
showError = false
errorText = nil
tooltipTitle = nil
tooltipContent = nil
transparentBackground = false
width = nil
maxLength = nil
inputId = nil
value = nil
defaultValue = nil
required = false
readOnly = false
accessibilityHintEnabled = nil
accessibilityHintDisabled = nil
accessibilityValueEnabled = nil
accessibilityValueDisabled = nil
accessibilityLabelEnabled = nil
accessibilityLabelDisabled = nil
setAccessibilityLabel()
}
@ -313,8 +351,6 @@ open class EntryField: Control, Accessable {
}
//set the titleLabel
titleLabel.textPosition = .left
titleLabel.typograpicalStyle = .BodySmall
titleLabel.text = updatedLabelText
titleLabel.attributes = attributes
titleLabel.surface = surface
@ -324,8 +360,6 @@ open class EntryField: Control, Accessable {
open func updateErrorLabel(){
if showError, let errorText {
errorLabel.textPosition = .left
errorLabel.typograpicalStyle = .BodySmall
errorLabel.text = errorText
errorLabel.surface = surface
errorLabel.disabled = disabled
@ -338,8 +372,6 @@ open class EntryField: Control, Accessable {
open func updateHelperLabel(){
//set the helper label position
if let helperText {
helperLabel.textPosition = .left
helperLabel.typograpicalStyle = .BodySmall
helperLabel.text = helperText
helperLabel.surface = surface
helperLabel.disabled = disabled

View File

@ -60,6 +60,8 @@ open class TextEntryFieldBase: EntryField {
private var successLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left
$0.typograpicalStyle = .BodySmall
}
internal var minWidthConstraint: NSLayoutConstraint?
@ -80,6 +82,18 @@ open class TextEntryFieldBase: EntryField {
}
public override func reset() {
super.reset()
successLabel.reset()
successLabel.textPosition = .left
successLabel.typograpicalStyle = .BodySmall
type = .text
showSuccess = false
successText = nil
helperTextPlacement = .bottom
}
open override func getContainer() -> UIView {
containerStackView.addArrangedSubview(containerView)
return containerStackView
@ -126,8 +140,6 @@ open class TextEntryFieldBase: EntryField {
successLabel.isHidden = true
} else if showSuccess, let successText {
successLabel.textPosition = .left
successLabel.typograpicalStyle = .BodySmall
successLabel.text = successText
successLabel.surface = surface
successLabel.disabled = disabled

View File

@ -58,32 +58,28 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable {
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var stackView: UIStackView = {
return UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .horizontal
$0.distribution = .fill
}
}()
private var stackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .horizontal
$0.distribution = .fill
}
private var label = Label()
private var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
}
private var toggleView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
}()
private var knobView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = .white
}
}()
private var toggleView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
}
private var knobView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = .white
}
//--------------------------------------------------
// MARK: - Configuration Properties
@ -298,6 +294,20 @@ open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable {
public override func reset() {
super.reset()
label.reset()
isSelected = false
isOn = false
isAnimated = true
showText = false
onText = "On"
offText = "Off"
textSize = .small
textWeight = .regular
textPosition = .left
inputId = nil
value = nil
toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
knobView.backgroundColor = knobColorConfiguration.getColor(self)
setAccessibilityLabel()

View File

@ -261,3 +261,23 @@ extension TypographicalStyle {
}
}
extension TypographicalStyle {
public static func style(for fontName: String, size: CGFloat) -> TypographicalStyle? {
//filter all styles by fontName
let styles = allCases.filter{$0.fontFace.fontName == fontName }.sorted { lhs, rhs in lhs.pointSize < rhs.pointSize }
//if there are no styles then return nil
guard styles.count > 0 else { return nil }
//if there is an exact match on a style with this pointSize then return it
if let style = styles.first(where: {$0.pointSize == size }) {
return style
} else if let largerIndex = styles.firstIndex(where: { $0.pointSize > size}) { //find the closet one to pointSize
return styles[max(largerIndex - 1, 0)]
} else { //return the last style
return styles.last!
}
}
}