From fb623627bf1467f1995bb9e7d12fa4ddde1da9f4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Feb 2024 15:38:25 -0600 Subject: [PATCH 01/12] fixed bug where someone could nil out attributedText and resetting the text with the same value wouldn't trigger setNeedsUpdate() Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 7ecb79cd..67e53abe 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -135,15 +135,15 @@ open class Label: UILabel, ViewProtocol, UserInfoable { override open var text: String? { get { _text } set { - if _text != newValue { + if _text != newValue || newValue != attributedText?.string { _text = newValue useAttributedText = false - attributes = nil + attributes?.removeAll() setNeedsUpdate() } } } - + /// Whether the View is enabled or not. open override var isEnabled: Bool { didSet { setNeedsUpdate() } } From aa30561b298ccfc36bb792702a7bc3e2581c2a2f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Feb 2024 09:33:39 -0600 Subject: [PATCH 02/12] refactored naming conventions Signed-off-by: Matt Bruce --- VDS/Components/Tilelet/TileletSubTitleModel.swift | 12 ++++++------ VDS/Components/TitleLockup/TitleLockup.swift | 2 +- .../TitleLockup/TitleLockupSubTitleModel.swift | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index dab4c81c..ae28d958 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -14,7 +14,7 @@ extension Tilelet { // MARK: - Enums //-------------------------------------------------- /// Enum used to describe the textStyle of the subTitle label. - public enum StandardStyle: String, EnumSubset { + public enum OtherStandardStyle: String, EnumSubset { case bodyLarge case bodyMedium case bodySmall @@ -28,7 +28,7 @@ extension Tilelet { public var text: String = "" /// 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. public var textAttributes: [any LabelAttributeModel]? @@ -40,13 +40,13 @@ extension Tilelet { // MARK: - Initializers //-------------------------------------------------- public init(text: String, + otherStandardStyle: OtherStandardStyle = .bodySmall, textColor: Use = .primary, - textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .bodySmall) { + textAttributes: [any LabelAttributeModel]? = nil) { self.text = text + self.otherStandardStyle = otherStandardStyle self.textAttributes = textAttributes self.textColor = textColor - self.standardStyle = standardStyle } //-------------------------------------------------- @@ -55,7 +55,7 @@ extension Tilelet { /// Converts this type of model to a TitleLockup.SubTitleModel. public func toTitleLockupSubTitleModel() -> TitleLockup.SubTitleModel { TitleLockup.SubTitleModel(text: text, - standardStyle: standardStyle.value, + otherStandardStyle: otherStandardStyle.value, textColor: textColor, textAttributes: textAttributes) } diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index dbfd8637..e11b674d 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -45,7 +45,7 @@ open class TitleLockup: View { //-------------------------------------------------- private var otherStandardStyle: OtherStandardStyle { if let subTitleModel, !subTitleModel.text.isEmpty { - return subTitleModel.standardStyle + return subTitleModel.otherStandardStyle } else if let eyebrowModel, !eyebrowModel.text.isEmpty { return eyebrowModel.standardStyle } else { diff --git a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift index 3ba6c7ff..4ac0922c 100644 --- a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift @@ -14,7 +14,7 @@ extension TitleLockup { public var text: String /// 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. public var textColor: Use @@ -26,19 +26,19 @@ extension TitleLockup { public var numberOfLines: Int public init(text: String, - standardStyle: OtherStandardStyle = .bodyLarge, + otherStandardStyle: OtherStandardStyle = .bodyLarge, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, numberOfLines: Int = 0) { self.text = text - self.standardStyle = standardStyle + self.otherStandardStyle = otherStandardStyle self.textColor = textColor self.textAttributes = textAttributes self.numberOfLines = numberOfLines } /// TextStyle used to render the text. - public var textStyle: TextStyle { standardStyle.value.regular } + public var textStyle: TextStyle { otherStandardStyle.value.regular } } From f4f6a005e40e99bb8b620cbeaada917d0be4a792 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 27 Feb 2024 09:47:50 -0600 Subject: [PATCH 03/12] put accessibility changes to method. Signed-off-by: Matt Bruce --- VDS/Components/Loader/Loader.swift | 37 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/VDS/Components/Loader/Loader.swift b/VDS/Components/Loader/Loader.swift index 90d589be..baf6ecdb 100644 --- a/VDS/Components/Loader/Loader.swift +++ b/VDS/Components/Loader/Loader.swift @@ -87,8 +87,26 @@ open class Loader: View { } invalidateIntrinsicContentSize() } - + 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.repeatCount = .infinity 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() { From d8e393049a8c4bc189e85d1f68e230396cfa122e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 27 Feb 2024 10:02:09 -0600 Subject: [PATCH 04/12] updated release notes and version Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++-- VDS/SupportingFiles/ReleaseNotes.txt | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b13f7df5..ef6acb80 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -1253,7 +1253,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 54; + CURRENT_PROJECT_VERSION = 55; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1290,7 +1290,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 54; + CURRENT_PROJECT_VERSION = 55; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 404e531b..e1cd12fd 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -1,3 +1,8 @@ +1.0.55 +---------------- +- ONEAPP-6305 - BadgeIndicator - Finished Development +- ONEAPP-6679 - TileContainer - Finished Development + 1.0.54 ---------------- - CXTDT-518373 Accessibility Voiceover is reading “Still Loading” after waiting for a short time in all the screens. From 4df689b27558182c8a1676a1e7ae59b789d0a227 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 12:14:12 -0600 Subject: [PATCH 05/12] added internalShowError/internalErrorText so that you won't overwrite external validators Signed-off-by: Matt Bruce --- .../TextFields/EntryFieldBase.swift | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index e66d31a5..8eed853c 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -153,27 +153,30 @@ open class EntryFieldBase: Control, Changeable { /// Whether not to show the error. 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. open override var state: UIControl.State { get { var state = super.state - if showError { + if showError || showInternalError { state.insert(.error) } return state } } - - private var _errorText: String? - + open var errorText: String? { - get { return _errorText } - set { - if let newValue { - _errorText = newValue - } else { - _errorText = nil - } + didSet { + updateContainerView() + updateErrorLabel() + setNeedsUpdate() + } + } + + internal var internalErrorText: String? { + didSet { updateContainerView() updateErrorLabel() setNeedsUpdate() @@ -365,7 +368,16 @@ open class EntryFieldBase: Control, Changeable { } 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.surface = surface errorLabel.isEnabled = isEnabled @@ -374,6 +386,15 @@ open class EntryFieldBase: Control, Changeable { icon.color = VDSColor.paletteBlack icon.surface = surface 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 { icon.isHidden = true errorLabel.isHidden = true From b907e0e001099d480fabcb714bb5cc5afc2c432b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 12:14:27 -0600 Subject: [PATCH 06/12] implemented new internal error handling Signed-off-by: Matt Bruce --- .../TextFields/TextArea/TextArea.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 8e5f8e58..d4f11a9e 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -112,6 +112,7 @@ open class TextArea: EntryFieldBase { get { textView.text } set { textView.text = newValue + value = newValue } } @@ -227,7 +228,7 @@ open class TextArea: EntryFieldBase { bottomStackView.addArrangedSubview(characterCounterLabel) return bottomView } - + /// Used to update any Accessibility properties. open override func updateAccessibility() { super.updateAccessibility() @@ -246,12 +247,13 @@ open class TextArea: EntryFieldBase { let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" if ((maxLength ?? 0) > 0) { if (count > (maxLength ?? 0)) { - showError = true - errorText = "You have exceeded the character limit." + showInternalError = true + internalErrorText = "You have exceeded the character limit." return countStr } else { - showError = false - errorText = nil + + showInternalError = false + internalErrorText = nil return ("\(countStr)" + "/" + "\(maxLength ?? 0)") } } else { @@ -303,7 +305,7 @@ extension TextArea: UITextViewDelegate { highlightCharacterOverflow() //setting the value and firing control event - value = textView.text + text = textView.text sendActions(for: .valueChanged) } else { textView.text.removeLast() @@ -311,7 +313,7 @@ extension TextArea: UITextViewDelegate { } } else { //setting the value and firing control event - value = textView.text + text = textView.text sendActions(for: .valueChanged) } } From 3ada7fa0375fbeb7347f6b05d2e8afcc3b1a10ba Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 12:14:35 -0600 Subject: [PATCH 07/12] updated property Signed-off-by: Matt Bruce --- VDS/Components/TextFields/InputField/InputField.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 589ba28f..83768a6b 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -83,6 +83,7 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { get { textField.text } set { textField.text = newValue + value = newValue } } From de973fd7b87b84ea80ea0e00a95ded23a0946f7b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:06:13 -0600 Subject: [PATCH 08/12] added formfieldable Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 8eed853c..121b1510 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -13,7 +13,7 @@ import Combine /// Base Class used to build out a Input controls. @objc(VDSEntryField) -open class EntryFieldBase: Control, Changeable { +open class EntryFieldBase: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Initializers From 98b30237b285557e00b392b80c5960691d1c41f2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 28 Feb 2024 13:04:11 -0600 Subject: [PATCH 09/12] updated labels to ensure string is valid Signed-off-by: Matt Bruce --- VDS/Components/Label/Attributes/AnyLabelAttribute.swift | 1 + VDS/Components/Label/Attributes/ColorLabelAttribute.swift | 2 ++ VDS/Components/Label/Attributes/LabelAttributeModel.swift | 4 ++++ .../Label/Attributes/StrikeThroughLabelAttribute.swift | 1 + VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift | 1 + VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift | 3 ++- 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Label/Attributes/AnyLabelAttribute.swift b/VDS/Components/Label/Attributes/AnyLabelAttribute.swift index b914c912..50e76ba7 100644 --- a/VDS/Components/Label/Attributes/AnyLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/AnyLabelAttribute.swift @@ -30,6 +30,7 @@ public struct AnyAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.removeAttribute(key, range: range) attributedString.addAttribute(key, value: value, range: range) } diff --git a/VDS/Components/Label/Attributes/ColorLabelAttribute.swift b/VDS/Components/Label/Attributes/ColorLabelAttribute.swift index 354fbc93..50229ab0 100644 --- a/VDS/Components/Label/Attributes/ColorLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/ColorLabelAttribute.swift @@ -31,6 +31,8 @@ public struct ColorLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } + var colorRange = range if length == 0 && location == 0 { colorRange = .init(location: location, length: attributedString.length) diff --git a/VDS/Components/Label/Attributes/LabelAttributeModel.swift b/VDS/Components/Label/Attributes/LabelAttributeModel.swift index 62e1bbd8..87708ffb 100644 --- a/VDS/Components/Label/Attributes/LabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/LabelAttributeModel.swift @@ -29,6 +29,10 @@ extension LabelAttributeModel { public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool { lhs.isEqual(rhs) } + + public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool { + true//range.location + range.length <= attributedString.string.count + } } public extension NSAttributedString { diff --git a/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift b/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift index 93650f2b..ba4c97ac 100644 --- a/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/StrikeThroughLabelAttribute.swift @@ -24,6 +24,7 @@ public struct StrikeThroughLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) attributedString.addAttribute(.baselineOffset, value: 0, range: range) } diff --git a/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift b/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift index ae3ea1e5..a161d580 100644 --- a/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/TextStyleLabelAttribute.swift @@ -44,6 +44,7 @@ public struct TextStyleLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: textStyle.font, range: range) if let textColor { diff --git a/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift b/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift index 0ca4e2f9..a2ad403e 100644 --- a/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift @@ -52,7 +52,8 @@ public struct UnderlineLabelAttribute: LabelAttributeModel { //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- - public func setAttribute(on attributedString: NSMutableAttributedString) { + public func setAttribute(on attributedString: NSMutableAttributedString) { + guard isValidRange(on: attributedString) else { return } attributedString.addAttribute(.underlineStyle, value: underlineValue.rawValue, range: range) if let color = color { attributedString.addAttribute(.underlineColor, value: color, range: range) From e7a1698602a44946f158c8bafd9b6d8e7def2244 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:08:33 -0600 Subject: [PATCH 10/12] removed test Signed-off-by: Matt Bruce --- VDS/Components/Label/Attributes/LabelAttributeModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Label/Attributes/LabelAttributeModel.swift b/VDS/Components/Label/Attributes/LabelAttributeModel.swift index 87708ffb..ac43472f 100644 --- a/VDS/Components/Label/Attributes/LabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/LabelAttributeModel.swift @@ -31,7 +31,7 @@ extension LabelAttributeModel { } public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool { - true//range.location + range.length <= attributedString.string.count + range.location + range.length <= attributedString.string.count } } From c75064e84e6f81b35d5bf7275c97fae37b68d64d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:19:51 -0600 Subject: [PATCH 11/12] added more logic to the text/value properties Signed-off-by: Matt Bruce --- VDS/Components/TextFields/EntryFieldBase.swift | 14 +++++++++++++- .../TextFields/InputField/InputField.swift | 7 +++++-- VDS/Components/TextFields/TextArea/TextArea.swift | 9 ++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 121b1510..6964ae0a 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -198,8 +198,20 @@ open class EntryFieldBase: Control, Changeable, FormFieldable { open var maxLength: Int? { didSet { setNeedsUpdate() } } open var inputId: String? { didSet { setNeedsUpdate() } } + + /// The text of this textField. + private var _value: AnyHashable? + open var value: AnyHashable? { + get { _value } + set { + if let newValue, newValue != _value { + _value = newValue + text = newValue as? String + } + setNeedsUpdate() + } + } - open var value: AnyHashable? { didSet { setNeedsUpdate() } } open var defaultValue: AnyHashable? { didSet { setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/InputField/InputField.swift b/VDS/Components/TextFields/InputField/InputField.swift index 83768a6b..ac350bc1 100644 --- a/VDS/Components/TextFields/InputField/InputField.swift +++ b/VDS/Components/TextFields/InputField/InputField.swift @@ -82,8 +82,11 @@ open class InputField: EntryFieldBase, UITextFieldDelegate { open override var text: String? { get { textField.text } set { - textField.text = newValue - value = newValue + if let newValue, newValue != text { + textField.text = newValue + value = newValue + } + setNeedsUpdate() } } diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index d4f11a9e..5fe4e68b 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -107,12 +107,15 @@ open class TextArea: EntryFieldBase { } } - /// The text of this textField. + /// The text of this textView open override var text: String? { get { textView.text } set { - textView.text = newValue - value = newValue + if let newValue, newValue != text { + textView.text = newValue + value = newValue + } + setNeedsUpdate() } } From 6cd8b31985091607221d499303247cb45702b710 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 14:38:17 -0600 Subject: [PATCH 12/12] refactored out TextView Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 + .../TextFields/TextArea/TextArea.swift | 169 ++---------------- .../TextFields/TextArea/TextView.swift | 150 ++++++++++++++++ 3 files changed, 169 insertions(+), 154 deletions(-) create mode 100644 VDS/Components/TextFields/TextArea/TextView.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 125d88a8..10b469ed 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ EA5F86C82A1BD99100BC83E4 /* TabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F86C72A1BD99100BC83E4 /* TabModel.swift */; }; EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */ = {isa = PBXBuildFile; fileRef = EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */; }; 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 */; }; 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 */; }; @@ -236,6 +237,7 @@ EA5F86C72A1BD99100BC83E4 /* TabModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabModel.swift; sourceTree = ""; }; EA5F86CB2A1D28B500BC83E4 /* ReleaseNotes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReleaseNotes.txt; sourceTree = ""; }; EA5F86CF2A1F936100BC83E4 /* TabsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsContainer.swift; sourceTree = ""; }; + EA6F330D2B911E9000BACAB9 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = ""; }; EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = ""; }; EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = ""; }; @@ -717,6 +719,7 @@ isa = PBXGroup; children = ( EA985C22296E033A00F2FF2E /* TextArea.swift */, + EA6F330D2B911E9000BACAB9 /* TextView.swift */, 186B2A892B88DA7F001AB71F /* TextAreaChangeLog.txt */, ); path = TextArea; @@ -1059,6 +1062,7 @@ EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */, + EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */, EA985C7D297DAED300F2FF2E /* Primitive.swift in Sources */, EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */, diff --git a/VDS/Components/TextFields/TextArea/TextArea.swift b/VDS/Components/TextFields/TextArea/TextArea.swift index 5fe4e68b..317748e4 100644 --- a/VDS/Components/TextFields/TextArea/TextArea.swift +++ b/VDS/Components/TextFields/TextArea/TextArea.swift @@ -35,7 +35,6 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- internal var minWidthConstraint: NSLayoutConstraint? internal var textViewHeightConstraint: NSLayoutConstraint? - internal var allowCharCount: Int = 0 internal var inputFieldStackView: UIStackView = { return UIStackView().with { @@ -204,11 +203,9 @@ open class TextArea: EntryFieldBase { minWidthConstraint?.isActive = true } + let characterError = getCharacterCounterText() if let maxLength, maxLength > 0 { - // allow - 20% of character limit - let overflowLimit = Double(maxLength) * 0.20 - allowCharCount = Int(overflowLimit) + maxLength - characterCounterLabel.text = getCharacterCounterText() + characterCounterLabel.text = characterError } else { characterCounterLabel.text = "" } @@ -245,22 +242,23 @@ open class TextArea: EntryFieldBase { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - private func getCharacterCounterText() -> String { + private func getCharacterCounterText() -> String? { let count = textView.text.count let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)" - if ((maxLength ?? 0) > 0) { - if (count > (maxLength ?? 0)) { + if let maxLength, maxLength > 0 { + if count > maxLength { showInternalError = true internalErrorText = "You have exceeded the character limit." return countStr } else { - showInternalError = false internalErrorText = nil - return ("\(countStr)" + "/" + "\(maxLength ?? 0)") + return ("\(countStr)" + "/" + "\(maxLength)") } } else { - return "" + showInternalError = false + internalErrorText = nil + return nil } } @@ -303,7 +301,11 @@ extension TextArea: UITextViewDelegate { } //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 { highlightCharacterOverflow() @@ -319,146 +321,5 @@ extension TextArea: UITextViewDelegate { text = textView.text 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() - - //-------------------------------------------------- - // 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 - } - } - - + } } diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift new file mode 100644 index 00000000..cc1567bf --- /dev/null +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -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() + + //-------------------------------------------------- + // 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 + } + } +} +