Merge branch 'develop' into mbruce/textArea-refactor-entryfield

# Conflicts:
#	VDS/Components/TextFields/EntryFieldBase.swift
#	VDS/Components/TextFields/TextArea/TextArea.swift

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-02-29 15:09:03 -06:00
commit 62338cbfc1
10 changed files with 272 additions and 219 deletions

View File

@ -68,6 +68,7 @@
EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; }; EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; };
EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; }; EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; };
EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; }; EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */; };
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6F330D2B911E9000BACAB9 /* TextView.swift */; };
EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; }; EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; };
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; }; EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; };
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; }; EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
@ -236,6 +237,7 @@
EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = "<group>"; }; EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = "<group>"; };
EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; }; EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = "<group>"; };
EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = "<group>"; }; EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = "<group>"; };
EA6F330D2B911E9000BACAB9 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; }; EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; }; EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; };
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; }; EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
@ -717,6 +719,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
EA985C22296E033A00F2FF2E /* TextArea.swift */, EA985C22296E033A00F2FF2E /* TextArea.swift */,
EA6F330D2B911E9000BACAB9 /* TextView.swift */,
186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */, 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */,
); );
path = TextArea; path = TextArea;
@ -1059,6 +1062,7 @@
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */, EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */,
EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */, EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */,
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */,
EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */,
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */,
EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */, EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */,
@ -1257,7 +1261,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES; BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 54; CURRENT_PROJECT_VERSION = 55;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@ -1294,7 +1298,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES; BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 54; CURRENT_PROJECT_VERSION = 55;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -87,8 +87,26 @@ open class Loader: View {
} }
invalidateIntrinsicContentSize() invalidateIntrinsicContentSize()
} }
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility()
// check to make sure VoiceOver is running
guard UIAccessibility.isVoiceOverRunning, isActive else {
loadingTimer?.invalidate()
loadingTimer = nil
return
}
// Focus VoiceOver on this view
UIAccessibility.post(notification: .layoutChanged, argument: self)
// setup timer for post
loadingTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
guard let self, self.isActive, self.isVisibleOnScreen else { return }
self.accessibilityLabel = "Still Loading"
UIAccessibility.post(notification: .announcement, argument: "Still Loading")
}
} }
//-------------------------------------------------- //--------------------------------------------------
@ -107,23 +125,6 @@ open class Loader: View {
rotation.duration = 0.5 rotation.duration = 0.5
rotation.repeatCount = .infinity rotation.repeatCount = .infinity
icon.layer.add(rotation, forKey: rotationLayerName) icon.layer.add(rotation, forKey: rotationLayerName)
// check to make sure VoiceOver is running
guard UIAccessibility.isVoiceOverRunning else {
loadingTimer?.invalidate()
loadingTimer = nil
return
}
// Focus VoiceOver on this view
UIAccessibility.post(notification: .layoutChanged, argument: self)
// setup timer for post
loadingTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
guard let self, self.isActive, self.isVisibleOnScreen else { return }
self.accessibilityLabel = "Still Loading"
UIAccessibility.post(notification: .announcement, argument: "Still Loading")
}
} }
private func stopAnimating() { private func stopAnimating() {

View File

@ -13,7 +13,7 @@ import Combine
/// Base Class used to build out a Input controls. /// Base Class used to build out a Input controls.
@objc(VDSEntryField) @objc(VDSEntryField)
open class EntryFieldBase: Control, Changeable, FormFieldable { open class EntryFieldBase: Control, Changeable, FormFieldable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -153,27 +153,30 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
/// Whether not to show the error. /// Whether not to show the error.
open var showError: Bool = false { didSet { setNeedsUpdate() } } open var showError: Bool = false { didSet { setNeedsUpdate() } }
/// Whether or not to show the internal error
internal var showInternalError: Bool = false { didSet { setNeedsUpdate() } }
/// Override UIControl state to add the .error state if showError is true. /// Override UIControl state to add the .error state if showError is true.
open override var state: UIControl.State { open override var state: UIControl.State {
get { get {
var state = super.state var state = super.state
if showError { if showError || showInternalError {
state.insert(.error) state.insert(.error)
} }
return state return state
} }
} }
private var _errorText: String?
open var errorText: String? { open var errorText: String? {
get { return _errorText } didSet {
set { updateContainerView()
if let newValue { updateErrorLabel()
_errorText = newValue setNeedsUpdate()
} else { }
_errorText = nil }
}
internal var internalErrorText: String? {
didSet {
updateContainerView() updateContainerView()
updateErrorLabel() updateErrorLabel()
setNeedsUpdate() setNeedsUpdate()
@ -195,8 +198,19 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
open var maxLength: Int? { didSet { setNeedsUpdate() } } open var maxLength: Int? { didSet { setNeedsUpdate() } }
open var inputId: String? { didSet { setNeedsUpdate() } } open var inputId: String? { didSet { setNeedsUpdate() } }
open var value: String? { didSet { setNeedsUpdate() } } /// The text of this textField.
private var _value: String?
open var value: String? {
get { _value }
set {
if let newValue, newValue != _value {
_value = newValue
text = newValue
}
setNeedsUpdate()
}
}
open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } }
@ -365,7 +379,16 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
} }
open func updateErrorLabel(){ open func updateErrorLabel(){
if showError, let errorText { if showError, showInternalError, let errorText, let internalErrorText {
errorLabel.text = [internalErrorText, errorText].joined(separator: "\n")
errorLabel.surface = surface
errorLabel.isEnabled = isEnabled
errorLabel.isHidden = false
icon.name = .error
icon.color = VDSColor.paletteBlack
icon.surface = surface
icon.isHidden = !isEnabled
} else if showError, let errorText {
errorLabel.text = errorText errorLabel.text = errorText
errorLabel.surface = surface errorLabel.surface = surface
errorLabel.isEnabled = isEnabled errorLabel.isEnabled = isEnabled
@ -374,6 +397,15 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
icon.color = VDSColor.paletteBlack icon.color = VDSColor.paletteBlack
icon.surface = surface icon.surface = surface
icon.isHidden = !isEnabled icon.isHidden = !isEnabled
} else if showInternalError, let internalErrorText {
errorLabel.text = internalErrorText
errorLabel.surface = surface
errorLabel.isEnabled = isEnabled
errorLabel.isHidden = false
icon.name = .error
icon.color = VDSColor.paletteBlack
icon.surface = surface
icon.isHidden = !isEnabled
} else { } else {
icon.isHidden = true icon.isHidden = true
errorLabel.isHidden = true errorLabel.isHidden = true

View File

@ -79,10 +79,14 @@ open class InputField: EntryFieldBase, UITextFieldDelegate {
open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } } open var fieldType: FieldType = .text { didSet { setNeedsUpdate() } }
/// The text of this textField. /// The text of this textField.
open override var value: String? { open override var text: String? {
get { textField.text } get { textField.text }
set { set {
textField.text = newValue if let newValue, newValue != text {
textField.text = newValue
value = newValue
}
setNeedsUpdate()
} }
} }

View File

@ -35,7 +35,6 @@ open class TextArea: EntryFieldBase {
//-------------------------------------------------- //--------------------------------------------------
internal var minWidthConstraint: NSLayoutConstraint? internal var minWidthConstraint: NSLayoutConstraint?
internal var textViewHeightConstraint: NSLayoutConstraint? internal var textViewHeightConstraint: NSLayoutConstraint?
internal var allowCharCount: Int = 0
internal var inputFieldStackView: UIStackView = { internal var inputFieldStackView: UIStackView = {
return UIStackView().with { return UIStackView().with {
@ -107,12 +106,17 @@ open class TextArea: EntryFieldBase {
} }
} }
open override var value: String? { /// The text of this textView
private var _text: String?
open override var text: String? {
get { textView.text } get { textView.text }
set { set {
textView.text = newValue if let newValue, newValue != _text {
_text = newValue
textView.text = newValue
value = newValue
}
setNeedsUpdate() setNeedsUpdate()
} }
} }
@ -201,17 +205,15 @@ open class TextArea: EntryFieldBase {
minWidthConstraint?.isActive = true minWidthConstraint?.isActive = true
} }
let characterError = getCharacterCounterText()
if let maxLength, maxLength > 0 { if let maxLength, maxLength > 0 {
// allow - 20% of character limit characterCounterLabel.text = characterError
let overflowLimit = Double(maxLength) * 0.20
allowCharCount = Int(overflowLimit) + maxLength
characterCounterLabel.text = getCharacterCounterText()
} else { } else {
characterCounterLabel.text = "" characterCounterLabel.text = ""
} }
showError = !validator.validate() showInternalError = !validator.validate()
errorText = validator.errorMessage internalErrorText = validator.errorMessage
icon.size = .medium icon.size = .medium
icon.color = iconColorConfiguration.getColor(self) icon.color = iconColorConfiguration.getColor(self)
@ -231,7 +233,7 @@ open class TextArea: EntryFieldBase {
bottomStackView.addArrangedSubview(characterCounterLabel) bottomStackView.addArrangedSubview(characterCounterLabel)
return bottomView return bottomView
} }
/// Used to update any Accessibility properties. /// Used to update any Accessibility properties.
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
@ -245,17 +247,17 @@ open class TextArea: EntryFieldBase {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Methods // MARK: - Private Methods
//-------------------------------------------------- //--------------------------------------------------
private func getCharacterCounterText() -> String { private func getCharacterCounterText() -> String? {
let count = textView.text.count let count = textView.text.count
let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)"
if ((maxLength ?? 0) > 0) { if let maxLength, maxLength > 0 {
if (count > (maxLength ?? 0)) { if count > maxLength {
return countStr return countStr
} else { } else {
return ("\(countStr)" + "/" + "\(maxLength ?? 0)") return ("\(countStr)" + "/" + "\(maxLength)")
} }
} else { } else {
return "" return nil
} }
} }
@ -289,16 +291,8 @@ open class TextArea: EntryFieldBase {
var errorMessage: String = "You have exceeded the character limit." var errorMessage: String = "You have exceeded the character limit."
func isValid(value: String?) -> Bool { func isValid(value: String?) -> Bool {
guard let text = value else { return true } guard let text = value, let maxLength, maxLength > 0 else { return true }
return text.count <= maxLength
if ((maxLength ?? 0) > 0) {
if (text.count > (maxLength ?? 0)) {
return false
} else {
return true
}
}
return true
} }
} }
} }
@ -325,12 +319,16 @@ extension TextArea: UITextViewDelegate {
} }
//The exceeding characters will be highlighted to help users correct their entry. //The exceeding characters will be highlighted to help users correct their entry.
if ((maxLength ?? 0) > 0) { if let maxLength, maxLength > 0 {
// allow - 20% of character limit
let overflowLimit = Double(maxLength) * 0.20
let allowCharCount = Int(overflowLimit) + maxLength
if textView.text.count <= allowCharCount { if textView.text.count <= allowCharCount {
highlightCharacterOverflow() highlightCharacterOverflow()
//setting the value and firing control event //setting the value and firing control event
value = textView.text text = textView.text
sendActions(for: .valueChanged) sendActions(for: .valueChanged)
} else { } else {
textView.text.removeLast() textView.text.removeLast()
@ -338,149 +336,8 @@ extension TextArea: UITextViewDelegate {
} }
} else { } else {
//setting the value and firing control event //setting the value and firing control event
value = textView.text text = textView.text
sendActions(for: .valueChanged) sendActions(for: .valueChanged)
} }
} }
}
/// Will move this into a new file, need to talk with Scott/Kyle
open class TextView: UITextView, ViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero, textContainer: nil)
initialSetup()
}
public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
initialSetup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
initialSetup()
}
//--------------------------------------------------
// MARK: - Combine Properties
//--------------------------------------------------
/// Set of Subscribers for any Publishers for this Control.
open var subscribers = Set<AnyCancellable>()
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var initialSetupPerformed = false
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
/// Array of LabelAttributeModel objects used in rendering the text.
open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } }
/// TextStyle used on the titleLabel.
open var textStyle: TextStyle { .defaultStyle }
/// Will determine if a scaled font should be used for the titleLabel font.
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
open var isEnabled: Bool = true { didSet { setNeedsUpdate() } }
open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with {
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
}.eraseToAnyColorable(){ didSet { setNeedsUpdate() }}
open override var textColor: UIColor? {
get { textColorConfiguration.getColor(self) }
set { }
}
override public var text: String! {
get { super.text }
set {
super.text = newValue
updateLabel()
}
}
override public var textAlignment: NSTextAlignment {
didSet {
if textAlignment != oldValue {
// Text alignment can be part of our paragraph style, so we may need to
// re-style when changed
updateLabel()
}
}
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open func initialSetup() {
if !initialSetupPerformed {
backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = []
setup()
setNeedsUpdate()
}
}
open func setup() {
translatesAutoresizingMaskIntoConstraints = false
}
open func updateView() {
updateLabel()
}
open func updateAccessibility() {}
open func reset() {
shouldUpdateView = false
surface = .light
text = nil
accessibilityCustomActions = []
shouldUpdateView = true
setNeedsUpdate()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
private func updateLabel() {
//clear the arrays holding actions
accessibilityCustomActions = []
if let text, !text.isEmpty {
//create the primary string
let mutableText = NSMutableAttributedString.mutableText(for: text,
textStyle: textStyle,
useScaledFont: useScaledFont,
textColor: textColor!,
alignment: textAlignment,
lineBreakMode: .byWordWrapping)
//apply any attributes
if let attributes = textAttributes {
mutableText.apply(attributes: attributes)
}
attributedText = mutableText
} else {
attributedText = nil
}
}
} }

View File

@ -0,0 +1,150 @@
//
// TextView.swift
// VDS
//
// Created by Matt Bruce on 2/29/24.
//
import Foundation
import UIKit
import Combine
import VDSColorTokens
/// Will move this into a new file, need to talk with Scott/Kyle
open class TextView: UITextView, ViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero, textContainer: nil)
initialSetup()
}
public override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
initialSetup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
initialSetup()
}
//--------------------------------------------------
// MARK: - Combine Properties
//--------------------------------------------------
/// Set of Subscribers for any Publishers for this Control.
open var subscribers = Set<AnyCancellable>()
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var initialSetupPerformed = false
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
/// Array of LabelAttributeModel objects used in rendering the text.
open var textAttributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() } }
/// TextStyle used on the titleLabel.
open var textStyle: TextStyle { .defaultStyle }
/// Will determine if a scaled font should be used for the titleLabel font.
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
open var isEnabled: Bool = true { didSet { setNeedsUpdate() } }
open var textColorConfiguration: AnyColorable = ViewColorConfiguration().with {
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
}.eraseToAnyColorable(){ didSet { setNeedsUpdate() }}
open override var textColor: UIColor? {
get { textColorConfiguration.getColor(self) }
set { }
}
override public var text: String! {
get { super.text }
set {
super.text = newValue
updateLabel()
}
}
override public var textAlignment: NSTextAlignment {
didSet {
if textAlignment != oldValue {
// Text alignment can be part of our paragraph style, so we may need to
// re-style when changed
updateLabel()
}
}
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
open func initialSetup() {
if !initialSetupPerformed {
backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = []
setup()
setNeedsUpdate()
}
}
open func setup() {
translatesAutoresizingMaskIntoConstraints = false
}
open func updateView() {
updateLabel()
}
open func updateAccessibility() {}
open func reset() {
shouldUpdateView = false
surface = .light
text = nil
accessibilityCustomActions = []
shouldUpdateView = true
setNeedsUpdate()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
private func updateLabel() {
//clear the arrays holding actions
accessibilityCustomActions = []
if let text, !text.isEmpty {
//create the primary string
let mutableText = NSMutableAttributedString.mutableText(for: text,
textStyle: textStyle,
useScaledFont: useScaledFont,
textColor: textColor!,
alignment: textAlignment,
lineBreakMode: .byWordWrapping)
//apply any attributes
if let attributes = textAttributes {
mutableText.apply(attributes: attributes)
}
attributedText = mutableText
} else {
attributedText = nil
}
}
}

View File

@ -14,7 +14,7 @@ extension Tilelet {
// MARK: - Enums // MARK: - Enums
//-------------------------------------------------- //--------------------------------------------------
/// Enum used to describe the textStyle of the subTitle label. /// Enum used to describe the textStyle of the subTitle label.
public enum StandardStyle: String, EnumSubset { public enum OtherStandardStyle: String, EnumSubset {
case bodyLarge case bodyLarge
case bodyMedium case bodyMedium
case bodySmall case bodySmall
@ -28,7 +28,7 @@ extension Tilelet {
public var text: String = "" public var text: String = ""
/// Text style that will be used for the subTitle label. /// Text style that will be used for the subTitle label.
public var standardStyle: StandardStyle = .bodySmall public var otherStandardStyle: OtherStandardStyle = .bodySmall
/// Text attributes that will be used for the subTitle label. /// Text attributes that will be used for the subTitle label.
public var textAttributes: [any LabelAttributeModel]? public var textAttributes: [any LabelAttributeModel]?
@ -40,13 +40,13 @@ extension Tilelet {
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
public init(text: String, public init(text: String,
otherStandardStyle: OtherStandardStyle = .bodySmall,
textColor: Use = .primary, textColor: Use = .primary,
textAttributes: [any LabelAttributeModel]? = nil, textAttributes: [any LabelAttributeModel]? = nil) {
standardStyle: StandardStyle = .bodySmall) {
self.text = text self.text = text
self.otherStandardStyle = otherStandardStyle
self.textAttributes = textAttributes self.textAttributes = textAttributes
self.textColor = textColor self.textColor = textColor
self.standardStyle = standardStyle
} }
//-------------------------------------------------- //--------------------------------------------------
@ -55,7 +55,7 @@ extension Tilelet {
/// Converts this type of model to a TitleLockup.SubTitleModel. /// Converts this type of model to a TitleLockup.SubTitleModel.
public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel { public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel {
TitleLockup.SubTitleModel(text: text, TitleLockup.SubTitleModel(text: text,
standardStyle: standardStyle.value, otherStandardStyle: otherStandardStyle.value,
textColor: textColor, textColor: textColor,
textAttributes: textAttributes) textAttributes: textAttributes)
} }

View File

@ -45,7 +45,7 @@ open class TitleLockup: View {
//-------------------------------------------------- //--------------------------------------------------
private var otherStandardStyle: OtherStandardStyle { private var otherStandardStyle: OtherStandardStyle {
if let subTitleModel, !subTitleModel.text.isEmpty { if let subTitleModel, !subTitleModel.text.isEmpty {
return subTitleModel.standardStyle return subTitleModel.otherStandardStyle
} else if let eyebrowModel, !eyebrowModel.text.isEmpty { } else if let eyebrowModel, !eyebrowModel.text.isEmpty {
return eyebrowModel.standardStyle return eyebrowModel.standardStyle
} else { } else {

View File

@ -14,7 +14,7 @@ extension TitleLockup {
public var text: String public var text: String
/// Standard style that will be used for the subTitle label. /// Standard style that will be used for the subTitle label.
public var standardStyle: OtherStandardStyle public var otherStandardStyle: OtherStandardStyle
/// Text color used in the subtitle label. /// Text color used in the subtitle label.
public var textColor: Use public var textColor: Use
@ -26,19 +26,19 @@ extension TitleLockup {
public var numberOfLines: Int public var numberOfLines: Int
public init(text: String, public init(text: String,
standardStyle: OtherStandardStyle = .bodyLarge, otherStandardStyle: OtherStandardStyle = .bodyLarge,
textColor: Use = .primary, textColor: Use = .primary,
textAttributes: [any LabelAttributeModel]? = nil, textAttributes: [any LabelAttributeModel]? = nil,
numberOfLines: Int = 0) { numberOfLines: Int = 0) {
self.text = text self.text = text
self.standardStyle = standardStyle self.otherStandardStyle = otherStandardStyle
self.textColor = textColor self.textColor = textColor
self.textAttributes = textAttributes self.textAttributes = textAttributes
self.numberOfLines = numberOfLines self.numberOfLines = numberOfLines
} }
/// TextStyle used to render the text. /// TextStyle used to render the text.
public var textStyle: TextStyle { standardStyle.value.regular } public var textStyle: TextStyle { otherStandardStyle.value.regular }
} }

View File

@ -1,3 +1,8 @@
1.0.55
----------------
- ONEAPP-6305 - BadgeIndicator - Finished Development
- ONEAPP-6679 - TileContainer - Finished Development
1.0.54 1.0.54
---------------- ----------------
- CXTDT-518373 Accessibility Voiceover is reading “Still Loading” after waiting for a short time in all the screens. - CXTDT-518373 Accessibility Voiceover is reading “Still Loading” after waiting for a short time in all the screens.