From 441971d2f917ff5ebdd97437fa4878e18dba9893 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 09:20:17 -0600 Subject: [PATCH 01/20] updated to fix issue for attributedText Signed-off-by: Matt Bruce --- .../Selector/SelectorItemBase.swift | 2 - VDS/Components/Label/Label.swift | 118 ++++++++++++------ VDS/Components/RadioBox/RadioBoxItem.swift | 3 - 3 files changed, 82 insertions(+), 41 deletions(-) diff --git a/VDS/BaseClasses/Selector/SelectorItemBase.swift b/VDS/BaseClasses/Selector/SelectorItemBase.swift index 9ca86d8c..869c40e0 100644 --- a/VDS/BaseClasses/Selector/SelectorItemBase.swift +++ b/VDS/BaseClasses/Selector/SelectorItemBase.swift @@ -102,7 +102,6 @@ open class SelectorItemBase: Control, Errorable, /// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes. open var labelAttributedText: NSAttributedString? { didSet { - label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true) label.attributedText = labelAttributedText setNeedsUpdate() } @@ -117,7 +116,6 @@ open class SelectorItemBase: Control, Errorable, /// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes. open var childAttributedText: NSAttributedString? { didSet { - childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true) childLabel.attributedText = childAttributedText setNeedsUpdate() } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 67e53abe..c998c70b 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -42,6 +42,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + private enum TextSetMode { + case text + case attributedText + } + + private var textSetMode: TextSetMode = .text + private var initialSetupPerformed = false private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets } @@ -102,10 +109,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- /// Key of whether or not updateView() is called in setNeedsUpdate() open var shouldUpdateView: Bool = true - - /// Determines if the label should use its own attributedText property instead of rendering the attributedText propert - /// based of other local properties, such as textStyle, textColor, surface, etc... The default value is false. - open var useAttributedText: Bool = false /// Will determine if a scaled font should be used for the font. open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }} @@ -132,15 +135,25 @@ open class Label: UILabel, ViewProtocol, UserInfoable { private var _text: String? /// Text that will be used in the label. - override open var text: String? { - get { _text } + override public var text: String! { + get { super.text } set { - if _text != newValue || newValue != attributedText?.string { - _text = newValue - useAttributedText = false - attributes?.removeAll() - setNeedsUpdate() - } + // When text is set, we may need to re-style it as attributedText + // with the correct paragraph style to achieve the desired line height. + textSetMode = .text + attributes?.removeAll() + styleText(newValue) + } + } + + ///AttributedText that will be used in the label. + override public var attributedText: NSAttributedString? { + get { super.attributedText } + set { + // When text is set, we may need to re-style it as attributedText + // with the correct paragraph style to achieve the desired line height. + textSetMode = .attributedText + styleAttributedText(newValue) } } @@ -200,30 +213,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable { } open func updateView() { - if !useAttributedText { - if let text { - accessibilityCustomActions = [] - - //create the primary string - let mutableText = NSMutableAttributedString.mutableText(for: text, - textStyle: textStyle, - useScaledFont: useScaledFont, - textColor: textColorConfiguration.getColor(self), - alignment: textAlignment, - lineBreakMode: lineBreakMode) - - applyAttributes(mutableText) - - //set the attributed text - attributedText = mutableText - - //force a drawText - setNeedsDisplay() - - setNeedsLayout() - layoutIfNeeded() - } - } + restyleText() + + //force a drawText + setNeedsDisplay() + + setNeedsLayout() + layoutIfNeeded() } open func updateAccessibility() { @@ -269,6 +265,56 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- + private func restyleText() { + if textSetMode == .text { + styleText(text) + } else { + styleAttributedText(attributedText) + } + } + + private func styleText(_ newValue: String!) { + defer { invalidateIntrinsicContentSize() } + guard let newValue else { + // We don't need to use attributed text + super.attributedText = nil + super.text = newValue + return + } + + accessibilityCustomActions = [] + + //create the primary string + let mutableText = NSMutableAttributedString.mutableText(for: newValue, + textStyle: textStyle, + useScaledFont: useScaledFont, + textColor: textColorConfiguration.getColor(self), + alignment: textAlignment, + lineBreakMode: lineBreakMode) + + applyAttributes(mutableText) + + // Set attributed text to match typography + super.attributedText = mutableText + + } + + private func styleAttributedText(_ newValue: NSAttributedString?) { + defer { invalidateIntrinsicContentSize() } + guard let newValue = newValue else { + // We don't need any additional styling + super.attributedText = newValue + return + } + + var mutableText = NSMutableAttributedString(attributedString: newValue) + + applyAttributes(mutableText) + + // Modify attributed text to match typography + super.attributedText = newValue + } + private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) { actions = [] diff --git a/VDS/Components/RadioBox/RadioBoxItem.swift b/VDS/Components/RadioBox/RadioBoxItem.swift index 81f3f709..cfeb54e6 100644 --- a/VDS/Components/RadioBox/RadioBoxItem.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -100,7 +100,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { /// If provided, the RadioBox textAttributedText will be rendered. open var textAttributedText: NSAttributedString? { didSet { - textLabel.useAttributedText = !(textAttributedText?.string.isEmpty ?? true) textLabel.attributedText = textAttributedText setNeedsUpdate() } @@ -115,7 +114,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { /// If provided, the RadioBox subTextAttributedText will be rendered. open var subTextAttributedText: NSAttributedString? { didSet { - subTextLabel.useAttributedText = !(subTextAttributedText?.string.isEmpty ?? true) subTextLabel.attributedText = subTextAttributedText setNeedsUpdate() } @@ -130,7 +128,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { /// If provided, the RadioBox subTextRightAttributedText will be rendered. open var subTextRightAttributedText: NSAttributedString? { didSet { - subTextRightLabel.useAttributedText = !(subTextRightAttributedText?.string.isEmpty ?? true) subTextRightLabel.attributedText = subTextRightAttributedText setNeedsUpdate() } From 0fffd3811396b7120575501620e7af1a71fe8cb2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:08:12 -0600 Subject: [PATCH 02/20] updated initialSetup Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 1 + VDS/Components/TextFields/TextArea/TextView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index 255c6f64..cc096d41 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -102,6 +102,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { + initialSetupPerformed = true backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index ea96ed6e..ffb355d1 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -94,6 +94,7 @@ open class TextView: UITextView, ViewProtocol { //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { + initialSetupPerformed = true backgroundColor = .clear translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] From 21ab644c16358f85f46a0e6996aff75a29b360c2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:08:32 -0600 Subject: [PATCH 03/20] refactored for custom fonts Signed-off-by: Matt Bruce --- VDS/Fonts/Font.swift | 11 +++++++++-- VDS/Fonts/FontProtocol.swift | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/VDS/Fonts/Font.swift b/VDS/Fonts/Font.swift index c5caaa32..6b875ad3 100644 --- a/VDS/Fonts/Font.swift +++ b/VDS/Fonts/Font.swift @@ -8,13 +8,14 @@ import Foundation /// Enum that is matched up for the Verizon fonts. -public enum Font: String, FontProtocol { +public enum Font: FontProtocol { case edsBold case edsRegular case dsLight case etxBold case etxRegular - + case custom(String) + public var fontName: String { switch self { case .edsBold: @@ -27,9 +28,15 @@ public enum Font: String, FontProtocol { return "VerizonNHGeTX-Bold" case .etxRegular: return "VerizonNHGeTX-Regular" + case .custom(let fontName): + return fontName } } + public static var allCases: [Font] { + [.edsBold, .edsRegular, .dsLight, .etxBold, .etxRegular] + } + /// File Extension for each of the Font enums. public var fontFileExtension: String { return "otf" diff --git a/VDS/Fonts/FontProtocol.swift b/VDS/Fonts/FontProtocol.swift index 9ee21939..fbca693b 100644 --- a/VDS/Fonts/FontProtocol.swift +++ b/VDS/Fonts/FontProtocol.swift @@ -9,9 +9,10 @@ import Foundation import UIKit /// Used in Classes that require Fonts -public protocol FontProtocol: CaseIterable, RawRepresentable, Hashable { +public protocol FontProtocol: Hashable { var fontFileExtension: String { get } var fontName: String { get } + static var allCases: [Self] { get } } extension FontProtocol { From 23f3f8b6bfe471b29ed8110785911f4e5ae4ae57 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:08:50 -0600 Subject: [PATCH 04/20] added a conversion of a UIFont to a TextStyle Signed-off-by: Matt Bruce --- VDS/Typography/Typogprahy+Styles.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/VDS/Typography/Typogprahy+Styles.swift b/VDS/Typography/Typogprahy+Styles.swift index ec144617..0cd6e614 100644 --- a/VDS/Typography/Typogprahy+Styles.swift +++ b/VDS/Typography/Typogprahy+Styles.swift @@ -209,6 +209,24 @@ extension TextStyle { boldMicro ] } + + public static func convert(font: UIFont) -> TextStyle { + var found: TextStyle? + + for textStyle in allCases { + if font.fontName == textStyle.fontFace.fontName && + font.pointSize == textStyle.pointSize { + found = textStyle + break + } + } + + guard let found else { + return TextStyle(rawValue: "Custom", fontFace: .custom(font.fontName), pointSize: font.pointSize) + } + + return found + } } extension TextStyle { From 89a4c5d397e7df17355d5869c14ac03445e12d69 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 10:09:59 -0600 Subject: [PATCH 05/20] updated label with overrides for textColor and font Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 35 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index c998c70b..0c89ae3f 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -131,32 +131,43 @@ open class Label: UILabel, ViewProtocol, UserInfoable { /// Line break mode for the label, default is set to word wrapping. open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} - - private var _text: String? - + /// Text that will be used in the label. - override public var text: String! { + override open var text: String! { get { super.text } set { - // When text is set, we may need to re-style it as attributedText - // with the correct paragraph style to achieve the desired line height. textSetMode = .text - attributes?.removeAll() styleText(newValue) } } ///AttributedText that will be used in the label. - override public var attributedText: NSAttributedString? { + override open var attributedText: NSAttributedString? { get { super.attributedText } set { - // When text is set, we may need to re-style it as attributedText - // with the correct paragraph style to achieve the desired line height. textSetMode = .attributedText styleAttributedText(newValue) } } + override open var font: UIFont! { + didSet { + if let font, initialSetupPerformed { + textStyle = TextStyle.convert(font: font) + } + setNeedsUpdate() + } + } + + override open var textColor: UIColor! { + didSet { + if let textColor, initialSetupPerformed { + textColorConfiguration = SurfaceColorConfiguration(textColor, textColor).eraseToAnyColorable() + } + setNeedsUpdate() + } + } + /// Whether the View is enabled or not. open override var isEnabled: Bool { didSet { setNeedsUpdate() } } @@ -175,13 +186,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- open func initialSetup() { if !initialSetupPerformed { + initialSetupPerformed = true //register for ContentSizeChanges NotificationCenter .Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification) .sink { [weak self] notification in self?.setNeedsUpdate() }.store(in: &subscribers) - backgroundColor = .clear numberOfLines = 0 lineBreakMode = .byWordWrapping @@ -307,7 +318,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { return } - var mutableText = NSMutableAttributedString(attributedString: newValue) + let mutableText = NSMutableAttributedString(attributedString: newValue) applyAttributes(mutableText) From 473ab531e86e354b420811f2d8be77216aeb9f3e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 12:34:07 -0600 Subject: [PATCH 06/20] fixed bug Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 0c89ae3f..60e88740 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -323,7 +323,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { applyAttributes(mutableText) // Modify attributed text to match typography - super.attributedText = newValue + super.attributedText = mutableText } private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) { From fbdcdfc2774428db1efa330e18645280d1da84ab Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 12:34:23 -0600 Subject: [PATCH 07/20] refactored method Signed-off-by: Matt Bruce --- VDS/Typography/Typogprahy+Styles.swift | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/VDS/Typography/Typogprahy+Styles.swift b/VDS/Typography/Typogprahy+Styles.swift index 0cd6e614..0136d3c4 100644 --- a/VDS/Typography/Typogprahy+Styles.swift +++ b/VDS/Typography/Typogprahy+Styles.swift @@ -211,20 +211,9 @@ extension TextStyle { } public static func convert(font: UIFont) -> TextStyle { - var found: TextStyle? - - for textStyle in allCases { - if font.fontName == textStyle.fontFace.fontName && - font.pointSize == textStyle.pointSize { - found = textStyle - break - } + guard let found = allCases.first(where: { font.fontName == $0.fontFace.fontName && font.pointSize == $0.pointSize} ) else { + return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize) } - - guard let found else { - return TextStyle(rawValue: "Custom", fontFace: .custom(font.fontName), pointSize: font.pointSize) - } - return found } } From fd641a85cf5d2814f759322f9f33ec64c2f3ad37 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 8 Mar 2024 12:34:31 -0600 Subject: [PATCH 08/20] updated to handle custom fonts Signed-off-by: Matt Bruce --- VDS/Fonts/Font.swift | 22 +++++++++++++++++++--- VDS/Fonts/FontProtocol.swift | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/VDS/Fonts/Font.swift b/VDS/Fonts/Font.swift index 6b875ad3..f4ba798d 100644 --- a/VDS/Fonts/Font.swift +++ b/VDS/Fonts/Font.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit /// Enum that is matched up for the Verizon fonts. public enum Font: FontProtocol { @@ -14,7 +15,7 @@ public enum Font: FontProtocol { case dsLight case etxBold case etxRegular - case custom(String) + case custom(UIFont) public var fontName: String { switch self { @@ -28,8 +29,8 @@ public enum Font: FontProtocol { return "VerizonNHGeTX-Bold" case .etxRegular: return "VerizonNHGeTX-Regular" - case .custom(let fontName): - return fontName + case .custom(let font): + return font.fontName } } @@ -41,4 +42,19 @@ public enum Font: FontProtocol { public var fontFileExtension: String { return "otf" } + + /// Returns a UIFont for the fontName and size given. + /// - Parameters: + /// - size: Size of the font + /// - Returns: UIFont for the fontName and Size. + public func font(ofSize size: CGFloat) -> UIFont{ + DispatchQueue.once(block: { self.register() }) + switch self { + case .custom(let font): + return font + default: + guard let found = UIFont(name: self.fontName, size: size) else { return .systemFont(ofSize: size) } + return found + } + } } diff --git a/VDS/Fonts/FontProtocol.swift b/VDS/Fonts/FontProtocol.swift index fbca693b..df6ac6a3 100644 --- a/VDS/Fonts/FontProtocol.swift +++ b/VDS/Fonts/FontProtocol.swift @@ -9,7 +9,7 @@ import Foundation import UIKit /// Used in Classes that require Fonts -public protocol FontProtocol: Hashable { +public protocol FontProtocol { var fontFileExtension: String { get } var fontName: String { get } static var allCases: [Self] { get } From 466dbb7c6a2d697020128a0b527931d62e079075 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 12:47:16 -0500 Subject: [PATCH 09/20] update for TitleLockup for label compression/hugging Signed-off-by: Matt Bruce --- VDS/Components/TitleLockup/TitleLockup.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index e11b674d..3802b529 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -68,6 +68,7 @@ open class TitleLockup: View { /// Label used to render the eyebrow model. open var eyebrowLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .vertical) } /// Model used in rendering the eyebrow label. @@ -77,6 +78,7 @@ open class TitleLockup: View { /// Label used to render the title model. open var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .vertical) $0.accessibilityTraits.insert([.header]) } @@ -87,6 +89,7 @@ open class TitleLockup: View { /// Label used to render the subtitle model. open var subTitleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.setContentHuggingPriority(.required, for: .vertical) } /// Model used in rendering the subtitle label. @@ -380,7 +383,7 @@ open class TitleLockup: View { } //pin the last view to the bottom of this view - previousView?.pinBottom(0, .defaultHigh) + previousView?.pinBottom(0) //debugging for borders eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green) From 4733a3a00c000300e6056d87fb0ec16d2cce3bba Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 12:47:38 -0500 Subject: [PATCH 10/20] refactored to not use KVO, but a publisher KVO Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index c50082c4..6f37308c 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -34,7 +34,6 @@ public final class SelfSizingCollectionView: UICollectionView { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- - private var contentSizeObservation: NSKeyValueObservation? private var collectionViewHeight: NSLayoutConstraint? private var anyCancellable: AnyCancellable? @@ -45,7 +44,6 @@ public final class SelfSizingCollectionView: UICollectionView { /// The natural size for the receiving view, considering only properties of the view itself. public override var intrinsicContentSize: CGSize { let contentSize = self.contentSize - //print(#function, contentSize) return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } @@ -68,17 +66,21 @@ public final class SelfSizingCollectionView: UICollectionView { setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical) collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() - - // Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews. - self.contentSizeObservation = self.observe(\.contentSize, options: [.old, .new]) { [weak self] _, change in - // If we don't specify `options: [.old, .new]`, the change.oldValue and .newValue will always be `nil`. - if change.newValue != change.oldValue { - self?.invalidateIntrinsicContentSize() - if let height = change.newValue?.height { - self?.collectionViewHeight?.constant = height + + anyCancellable = self.publisher(for: \.contentSize, options: [.old, .new]) + .scan((old: CGSize.zero, new: CGSize.zero)) { accumulator, newValue in + // accumulator.old contains the old contentSize value + // accumulator.new contains the new contentSize value before the current update + // newValue is the current update to contentSize + return (old: accumulator.new, new: newValue) + } + .sink { [weak self] compare in + if compare.old != compare.new { + print("Old contentSize: \(compare.old), New contentSize: \(compare.new)") + self?.invalidateIntrinsicContentSize() + self?.collectionViewHeight?.constant = compare.new.height } } - } } } From 73c7e23b99bb099835323b8ea463a76b0acc71c7 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 12:56:14 -0500 Subject: [PATCH 11/20] removed print Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index 6f37308c..d8656490 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -76,7 +76,6 @@ public final class SelfSizingCollectionView: UICollectionView { } .sink { [weak self] compare in if compare.old != compare.new { - print("Old contentSize: \(compare.old), New contentSize: \(compare.new)") self?.invalidateIntrinsicContentSize() self?.collectionViewHeight?.constant = compare.new.height } From 6aa09da464b7693edf207920193f378b2287a355 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 13:51:19 -0500 Subject: [PATCH 12/20] refactored to have a public contentSize publisher Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 26 +++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index d8656490..6f4de7d0 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -36,7 +36,15 @@ public final class SelfSizingCollectionView: UICollectionView { //-------------------------------------------------- private var collectionViewHeight: NSLayoutConstraint? private var anyCancellable: AnyCancellable? - + private var contentSizeSubject = CurrentValueSubject(.zero) + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + public var contentSizePublisher: AnyPublisher { + contentSizeSubject.eraseToAnyPublisher() + } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -67,17 +75,13 @@ public final class SelfSizingCollectionView: UICollectionView { setContentCompressionResistancePriority(.required, for: .vertical) collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() - anyCancellable = self.publisher(for: \.contentSize, options: [.old, .new]) - .scan((old: CGSize.zero, new: CGSize.zero)) { accumulator, newValue in - // accumulator.old contains the old contentSize value - // accumulator.new contains the new contentSize value before the current update - // newValue is the current update to contentSize - return (old: accumulator.new, new: newValue) - } + anyCancellable = self.publisher(for: \.contentSize, options: [.new]) .sink { [weak self] compare in - if compare.old != compare.new { - self?.invalidateIntrinsicContentSize() - self?.collectionViewHeight?.constant = compare.new.height + guard let self else { return } + if compare.height != self.collectionViewHeight?.constant { + self.invalidateIntrinsicContentSize() + self.collectionViewHeight?.constant = compare.height + self.contentSizeSubject.send(compare) } } } From 59b72835c08b05b2c2012b9f14adc4168ec94a9a Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 13:51:32 -0500 Subject: [PATCH 13/20] include new contentSizePublisher Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift index 7d506961..a51371c2 100644 --- a/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift +++ b/VDS/Components/Buttons/ButtonGroup/ButtonGroup.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import VDSColorTokens import VDSFormControlsTokens +import Combine /// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings. @objc(VDSButtonGroup) @@ -98,6 +99,8 @@ open class ButtonGroup: View { buttons.forEach { $0.surface = surface } } } + + open var contentSizePublisher: AnyPublisher { collectionView.contentSizePublisher } //-------------------------------------------------- // MARK: - Private Properties @@ -108,6 +111,7 @@ open class ButtonGroup: View { $0.delegate = self } + /// CollectionView that renders the array of buttonBase obects. fileprivate lazy var collectionView: SelfSizingCollectionView = { return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with { From 1fa8f485f8a92e5dc9ba8bf8e8eb5fb52ce092d2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 11 Mar 2024 15:43:55 -0500 Subject: [PATCH 14/20] updated constraint to defaultHigh Signed-off-by: Matt Bruce --- VDS/Classes/SelfSizingCollectionView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Classes/SelfSizingCollectionView.swift b/VDS/Classes/SelfSizingCollectionView.swift index 6f4de7d0..ad5d5661 100644 --- a/VDS/Classes/SelfSizingCollectionView.swift +++ b/VDS/Classes/SelfSizingCollectionView.swift @@ -73,7 +73,7 @@ public final class SelfSizingCollectionView: UICollectionView { //ensure autoLayout uses intrinsic height setContentHuggingPriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical) - collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() + collectionViewHeight = height(constant: 0, priority: .defaultHigh) anyCancellable = self.publisher(for: \.contentSize, options: [.new]) .sink { [weak self] compare in From 42dccb47805276eb88f37431f139143ca9ee0e5d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 12 Mar 2024 09:55:02 -0500 Subject: [PATCH 15/20] add image helper Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++++ VDS/Extensions/UIImage+Helper.swift | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 VDS/Extensions/UIImage+Helper.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index b6a3d192..63549b92 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -132,6 +132,7 @@ EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068912A560B65002E3A2D /* LoaderViewController.swift */; }; EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; + EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE785302BA0A438009428EA /* UIImage+Helper.swift */; }; EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */; }; EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */; }; EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */; }; @@ -303,6 +304,7 @@ EAD068912A560B65002E3A2D /* LoaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderViewController.swift; sourceTree = ""; }; EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderLaunchable.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; + EAE785302BA0A438009428EA /* UIImage+Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helper.swift"; sourceTree = ""; }; EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BadgeChangeLog.txt; sourceTree = ""; }; EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonChangeLog.txt; sourceTree = ""; }; EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LineChangeLog.txt; sourceTree = ""; }; @@ -536,6 +538,7 @@ EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */, EA33623D2892EE950071C351 /* UIDevice.swift */, EA0B18092AA78F9000F2D0CD /* UIEdgeInsets.swift */, + EAE785302BA0A438009428EA /* UIImage+Helper.swift */, EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */, EA8E40902A7D3F6300934ED3 /* UIView+Accessibility.swift */, EAB5FED329267EB300998C17 /* UIView+NSLayoutConstraint.swift */, @@ -1025,6 +1028,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, diff --git a/VDS/Extensions/UIImage+Helper.swift b/VDS/Extensions/UIImage+Helper.swift new file mode 100644 index 00000000..c8f103dc --- /dev/null +++ b/VDS/Extensions/UIImage+Helper.swift @@ -0,0 +1,26 @@ +// +// UIImage+Icon.swift +// VDS +// +// Created by Matt Bruce on 3/11/24. +// + +import Foundation +import UIKit + +extension UIImage { + + /// UIImage helper for finding images based on the Icon.Name which uses the internal BundleManager. + /// - Parameters: + /// - name: RawRepresentable. + /// - color: Color to Tint the image with + /// - renderingMode: UIImage Rendering mode. + /// - Returns: UIImage for this proecess + public static func image(representing representable: T, color: UIColor? = nil, renderingMode: UIImage.RenderingMode = .alwaysOriginal) -> UIImage? where T.RawValue == String { + guard let image = BundleManager.shared.image(for: representable.rawValue) else { return nil } + + guard let color else { return image } + + return image.withTintColor(color, renderingMode: renderingMode) + } +} From 8369403cbe9737f6b679c089752d21ddddb80101 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 12 Mar 2024 09:57:42 -0500 Subject: [PATCH 16/20] refactored for the iconName image helper Signed-off-by: Matt Bruce --- VDS/Components/Icon/Icon.swift | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/VDS/Components/Icon/Icon.swift b/VDS/Components/Icon/Icon.swift index 5ac143f6..f52279f1 100644 --- a/VDS/Components/Icon/Icon.swift +++ b/VDS/Components/Icon/Icon.swift @@ -44,7 +44,7 @@ open class Icon: View { //-------------------------------------------------- /// UIImageView used to render the icon. open var imageView = UIImageView().with { - $0.isAccessibilityElement = false + $0.isAccessibilityElement = false $0.translatesAutoresizingMaskIntoConstraints = false $0.contentMode = .scaleAspectFill $0.clipsToBounds = true @@ -109,8 +109,8 @@ open class Icon: View { //get the image name //set the image - if let name, let image = getImage(for: name.rawValue) { - setImage(image: image, imageColor: imageColor) + if let name, let image = UIImage.image(for: name, color: imageColor) { + imageView.image = image } else { imageView.image = nil } @@ -129,17 +129,17 @@ open class Icon: View { super.updateAccessibility() accessibilityLabel = name?.rawValue ?? "icon" } - - //-------------------------------------------------- - // MARK: - Private Methods - //-------------------------------------------------- - private func getImage(for imageName: String) -> UIImage? { - - return BundleManager.shared.image(for: imageName) - } - - private func setImage(image: UIImage, imageColor: UIColor) { - imageView.image = image.withTintColor(imageColor) - } } +extension UIImage { + + /// UIImage helper for finding images based on the Icon.Name which uses the internal BundleManager. + /// - Parameters: + /// - name: Icon.Name rawRepresentable. + /// - color: Color to Tint the image with + /// - renderingMode: UIImage Rendering mode. + /// - Returns: UIImage for this proecess + public static func image(for iconName: Icon.Name, color: UIColor? = nil, renderingMode: UIImage.RenderingMode = .alwaysOriginal) -> UIImage? { + image(representing: iconName, color: color, renderingMode: renderingMode) + } +} From b4765bf0b4da433734205edc0b457550359598d2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Mar 2024 10:41:06 -0500 Subject: [PATCH 17/20] fixed bug for the Label Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 60e88740..db9804d0 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -133,10 +133,12 @@ open class Label: UILabel, ViewProtocol, UserInfoable { open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} /// Text that will be used in the label. + private var _text: String! override open var text: String! { - get { super.text } + get { _text } set { textSetMode = .text + _text = newValue styleText(newValue) } } @@ -278,7 +280,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- private func restyleText() { if textSetMode == .text { - styleText(text) + styleText(_text) } else { styleAttributedText(attributedText) } @@ -304,10 +306,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable { lineBreakMode: lineBreakMode) applyAttributes(mutableText) - + // Set attributed text to match typography super.attributedText = mutableText - } private func styleAttributedText(_ newValue: NSAttributedString?) { From d7435068a3b9117388c22b89769ea4ccad73d739 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Mar 2024 10:41:06 -0500 Subject: [PATCH 18/20] fixed bug for the Label Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 60e88740..db9804d0 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -133,10 +133,12 @@ open class Label: UILabel, ViewProtocol, UserInfoable { open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} /// Text that will be used in the label. + private var _text: String! override open var text: String! { - get { super.text } + get { _text } set { textSetMode = .text + _text = newValue styleText(newValue) } } @@ -278,7 +280,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { //-------------------------------------------------- private func restyleText() { if textSetMode == .text { - styleText(text) + styleText(_text) } else { styleAttributedText(attributedText) } @@ -304,10 +306,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable { lineBreakMode: lineBreakMode) applyAttributes(mutableText) - + // Set attributed text to match typography super.attributedText = mutableText - } private func styleAttributedText(_ newValue: NSAttributedString?) { From 547b03ac1cd5e47f400e34b22f00f9e1d8c03cfd Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 14 Mar 2024 18:42:33 -0500 Subject: [PATCH 19/20] fixed issue with attributedText setting not working correctly Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index db9804d0..fd77dc7e 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -108,14 +108,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable { // MARK: - Public Properties //-------------------------------------------------- /// Key of whether or not updateView() is called in setNeedsUpdate() - open var shouldUpdateView: Bool = true + open var shouldUpdateView: Bool = true /// Will determine if a scaled font should be used for the font. open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }} open var surface: Surface = .light { didSet { setNeedsUpdate() }} - /// Array of LabelAttributeModel objects used in rendering the text. + /// Array of LabelAttributeModel objects used in rendering the text. open var attributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} /// TextStyle used on the this label. @@ -135,20 +135,18 @@ open class Label: UILabel, ViewProtocol, UserInfoable { /// Text that will be used in the label. private var _text: String! override open var text: String! { - get { _text } - set { + didSet { + _text = text textSetMode = .text - _text = newValue - styleText(newValue) + setNeedsUpdate() } } ///AttributedText that will be used in the label. override open var attributedText: NSAttributedString? { - get { super.attributedText } - set { + didSet { textSetMode = .attributedText - styleAttributedText(newValue) + setNeedsUpdate() } } @@ -306,7 +304,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { lineBreakMode: lineBreakMode) applyAttributes(mutableText) - + // Set attributed text to match typography super.attributedText = mutableText } @@ -322,7 +320,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { let mutableText = NSMutableAttributedString(attributedString: newValue) applyAttributes(mutableText) - + // Modify attributed text to match typography super.attributedText = mutableText } @@ -424,3 +422,5 @@ open class Label: UILabel, ViewProtocol, UserInfoable { } } } + + From 60f71a7709733f9304de0082c1fd3d859b95150d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 14 Mar 2024 18:49:21 -0500 Subject: [PATCH 20/20] added string empty checks. Signed-off-by: Matt Bruce --- VDS/Components/Label/Label.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index fd77dc7e..955d9604 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -286,7 +286,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { private func styleText(_ newValue: String!) { defer { invalidateIntrinsicContentSize() } - guard let newValue else { + guard let newValue, !newValue.isEmpty else { // We don't need to use attributed text super.attributedText = nil super.text = newValue @@ -311,7 +311,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable { private func styleAttributedText(_ newValue: NSAttributedString?) { defer { invalidateIntrinsicContentSize() } - guard let newValue = newValue else { + guard let newValue, !newValue.string.isEmpty else { // We don't need any additional styling super.attributedText = newValue return