From 6e74c5e9b318201d753f361cee2b2849fc52e1a6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Dec 2023 15:07:52 -0600 Subject: [PATCH 01/17] added enum codable Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift index 1d24a0bd..8831c7c6 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -17,10 +17,11 @@ extension TileContainer.BackgroundColor: Codable {} extension TileContainer.Padding: Codable {} extension TileContainer.AspectRatio: Codable {} extension TextLink.Size: Codable {} +extension VDS.TitleLockup.TextAlignment: Codable {} extension VDS.Line.Style: Codable {} extension VDS.Line.Orientation: Codable {} extension Use: Codable {} extension VDS.Button.Size: RawRepresentableCodable { public static var mapping: [String : VDS.Button.Size] { ["standard": .large, "tiny": .small] } public static var defaultValue: VDS.Button.Size? { nil } -} \ No newline at end of file +} From 55b6b491afcad0c8ceb390a739413f317eb40f0f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Dec 2023 15:28:58 -0600 Subject: [PATCH 02/17] initial cut for TitleLockup and model Signed-off-by: Matt Bruce --- .../LockUps/TitleLockup.swift | 115 ++---------- .../LockUps/TitleLockupModel.swift | 174 +++++------------- 2 files changed, 60 insertions(+), 229 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift index b48e4ddb..b0fd7fea 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift @@ -5,119 +5,42 @@ // Created by Nadigadda, Sumanth on 04/05/22. // Copyright © 2022 Verizon Wireless. All rights reserved. // +import VDS + +@objcMembers open class TitleLockup: VDS.TitleLockup, VDSMoleculeViewProtocol { -@objcMembers open class TitleLockup: View { //-------------------------------------------------- - // MARK: - Outlets + // MARK: - Public Properties //-------------------------------------------------- + open var viewModel: TitleLockupModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? - public let eyebrow = Label(fontStyle: .RegularBodySmall) - public let title = Label(fontStyle: .RegularBodySmall) - public let subTitle = Label(fontStyle: .RegularBodySmall) - public lazy var stack: UIStackView = { - let stack = UIStackView(arrangedSubviews: [eyebrow, title, subTitle]) - stack.translatesAutoresizingMaskIntoConstraints = false - stack.axis = .vertical - return stack - }() - - var castModel: TitleLockupModel? { - get { return model as? TitleLockupModel } + //-------------------------------------------------- + // MARK: - Public Functions + //-------------------------------------------------- + open func viewModelDidUpdate() { + surface = viewModel.surface + eyebrowModel = viewModel.eyebrowModel(delegateObject: delegateObject, additionalData: additionalData) + titleModel = viewModel.titleModel(delegateObject: delegateObject, additionalData: additionalData) + subTitleModel = viewModel.subTitleModel(delegateObject: delegateObject, additionalData: additionalData) } - + //-------------------------------------------------- // MARK: - Initialization //-------------------------------------------------- - public convenience init() { + public convenience required init() { self.init(frame: .zero) } - //-------------------------------------------------- - // MARK: - MFViewProtocol - //-------------------------------------------------- - - open override func setupView() { - super.setupView() - addSubview(stack) - NSLayoutConstraint.constraintPinSubview(toSuperview: stack) - } - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - stack.updateView(size) - } - //-------------------------------------------------- // MARK: - MoleculeViewProtocol //-------------------------------------------------- - - open override func reset() { - super.reset() - stack.reset() - } - - //-------------------------------------------------- - // MARK: - MoleculeViewProtocol - //-------------------------------------------------- - - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.set(with: model, delegateObject, additionalData) - guard let model = model as? TitleLockupModel else { return } - stack.setCustomSpacing(model.defaultEyebrowTitleSpacing(), after: eyebrow) - stack.setCustomSpacing(model.defaultTitleSubTitleSpacing(), after: title) - stack.updateContainedMolecules(with: [model.eyebrow, - model.title, - model.subTitle], - delegateObject, additionalData) - } - - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 65 } - //-------------------------------------------------- - // MARK: - Accessibility Helpers - //-------------------------------------------------- - - /// Returns the labels text in one message. - func getAccessibilityMessage() -> String? { - - var message = "" - - if let eyebrowLabel = eyebrow.text { - message += eyebrowLabel + ", " - } - - if let headlineLabel = title.text { - message += headlineLabel + ", " - } - - if let bodyLabel = subTitle.text { - message += bodyLabel - } - - return message.count > 0 ? message : nil - } - - /// Returns an array of the appropriate accessibility elements. - func getAccessibilityElements() -> [Any]? { - - var elements: [UIView] = [] - - if eyebrow.hasText { - elements.append(eyebrow) - } - - if title.hasText { - elements.append(title) - } - - if subTitle.hasText { - elements.append(subTitle) - } - - return elements.count > 0 ? elements : nil - } + open func updateView(_ size: CGFloat) {} } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift index 89d32ca9..fb930bde 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift @@ -7,6 +7,7 @@ // import VDSColorTokens +import VDS public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtocol { @@ -22,43 +23,10 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco public var title: LabelModel public var subTitle: LabelModel? - public var alignment: Alignment = .left { - didSet { - ///Updating the text alignment for all labels - if let textAlignment = NSTextAlignment(rawValue: alignment.rawValue) { - eyebrow?.textAlignment = textAlignment - title.textAlignment = textAlignment - subTitle?.textAlignment = textAlignment - } - } - } + public var alignment: VDS.TitleLockup.TextAlignment = .left + public var inverted: Bool = false - public var inverted: Bool = false { - didSet { - ///Updating the text color - eyebrow?.textColor = titleColor - title.textColor = titleColor - subTitle?.textColor = subTitleColor - } - } - - private var _backgroundColor: Color? - public var backgroundColor: Color? { - get { - return inverted ? Color(uiColor: VDSColor.backgroundPrimaryDark) : Color(uiColor: VDSColor.backgroundPrimaryLight) - } - set { - _backgroundColor = newValue - } - } - - public var titleColor: Color? { - return inverted ? Color(uiColor: VDSColor.elementsPrimaryOndark) : Color(uiColor: VDSColor.elementsPrimaryOnlight) - } - - public var subTitleColor: Color? { - return inverted ? Color(uiColor: VDSColor.elementsSecondaryOndark) : Color(uiColor: VDSColor.elementsSecondaryOnlight) - } + public var backgroundColor: Color? public var children: [MoleculeModelProtocol] { [eyebrow, title, subTitle].compactMap { (molecule: MoleculeModelProtocol?) in molecule } @@ -72,58 +40,6 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco self.eyebrow = eyebrow self.title = title self.subTitle = subTitle - updateLabelAttributes() - } - - //-------------------------------------------------- - // MARK: - Enum - //-------------------------------------------------- - - public enum Alignment: String, Codable { - case left - case center - } - - //-------------------------------------------------- - // MARK: - Styling - //-------------------------------------------------- - - /// Returns the default fontStyle for the subtitle, based on the title fontStyle. - func defaultSubtitleFontStyle() -> Styler.Font { - switch title.fontStyle { - case .RegularTitleXLarge, .RegularTitle2XLarge, .RegularFeatureXSmall: - return .RegularBodyLarge - case .RegularFeatureSmall, .RegularFeatureMedium: - return .RegularTitleLarge - default: - return .RegularBodySmall - } - } - - /// Returns the default spacing between the eyebrow and title, based on the title fontStyle. - func defaultEyebrowTitleSpacing() -> CGFloat { - switch title.fontStyle { - case .RegularTitleXLarge, .RegularTitle2XLarge, .RegularFeatureXSmall, .RegularFeatureSmall: - return Padding.Three - case .RegularFeatureMedium: - return subTitle?.fontStyle == .RegularBodyLarge ? Padding.Three : Padding.Four - default: - return Padding.Two - } - } - - /// Returns the default spacing between the title and subTitle, based on the title fontStyle. - func defaultTitleSubTitleSpacing() -> CGFloat { - switch title.fontStyle { - case .RegularTitleXLarge: - return Padding.Three - case .RegularTitle2XLarge, .RegularFeatureXSmall, .RegularFeatureSmall: - return Padding.Four - case .RegularFeatureMedium: - return Padding.Five - default: - return Padding.Two - } } //-------------------------------------------------- @@ -133,7 +49,6 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco private enum CodingKeys: String, CodingKey { case id case moleculeName - case backgroundColor case eyebrow case title case subTitle @@ -148,21 +63,17 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString - title = try typeContainer.decodeMolecule(codingKey: .title) + title = try typeContainer.decode(LabelModel.self, forKey: .title) eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) - subTitle = try typeContainer.decodeMoleculeIfPresent(codingKey: .subTitle) + subTitle = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .subTitle) - if let newAlignment = try typeContainer.decodeIfPresent(Alignment.self, forKey: .alignment) { + if let newAlignment = try typeContainer.decodeIfPresent(VDS.TitleLockup.TextAlignment.self, forKey: .alignment) { alignment = newAlignment } if let invertedStatus = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { inverted = invertedStatus } - - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - - updateLabelAttributes() } public func encode(to encoder: Encoder) throws { @@ -174,46 +85,43 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco try container.encodeIfPresent(subTitle, forKey: .subTitle) try container.encode(alignment, forKey: .alignment) try container.encode(inverted, forKey: .inverted) - try container.encodeIfPresent(_backgroundColor, forKey: .backgroundColor) } - //-------------------------------------------------- - // MARK: - Model updates - //-------------------------------------------------- + public func eyebrowModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.EyebrowModel? { + guard let subTitle else { return nil } + let attrs = subTitle.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) + let style: TextStyle? = subTitle.fontStyle?.vdsTextStyle() + if let style, let standardStyle = VDS.TitleLockup.OtherStandardStyle(rawValue: style.toStandardStyle().rawValue) { + return .init(text: subTitle.text, isBold: style.isBold, standardStyle: standardStyle, textAttributes: attrs) + } else { + return .init(text: subTitle.text, textAttributes: attrs) + } + } + + public func titleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.TitleModel? { + let attrs = title.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) + let style: TextStyle? = title.fontStyle?.vdsTextStyle() + if let style, let standardStyle = VDS.TitleLockup.TitleStandardStyle(rawValue: style.toStandardStyle().rawValue) { + return .init(text: title.text, textAttributes: attrs, isBold: style.isBold, standardStyle: standardStyle) + } else { + return .init(text: title.text, textAttributes: attrs) + } + } - private func updateLabelAttributes() { - // If subtitle style is not available, will set font style based on the component - if subTitle?.fontStyle == nil { - subTitle?.fontStyle = defaultSubtitleFontStyle() - } - - // If eyebrow style is not available, will set font style based on the component. Eyebrow and subtitle share the same font size - if eyebrow?.fontStyle == nil { - eyebrow?.fontStyle = subTitle?.fontStyle - } - - // Updating the text color - if eyebrow?.textColor == nil { - eyebrow?.textColor = subTitleColor - } - if title.textColor == nil { - title.textColor = titleColor - } - if subTitle?.textColor == nil { - subTitle?.textColor = subTitleColor - } - - // Updating the text alignment for all labels - if let textAlignment = NSTextAlignment(rawValue: alignment.rawValue) { - if eyebrow?.textAlignment == nil { - eyebrow?.textAlignment = textAlignment - } - if title.textAlignment == nil { - title.textAlignment = textAlignment - } - if subTitle?.textAlignment == nil { - subTitle?.textAlignment = textAlignment - } + public func subTitleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.SubTitleModel? { + guard let subTitle else { return nil } + let attrs = subTitle.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) + let style: TextStyle? = subTitle.fontStyle?.vdsTextStyle() + if let style, let standardStyle = VDS.TitleLockup.OtherStandardStyle(rawValue: style.toStandardStyle().rawValue) { + return .init(text: subTitle.text, standardStyle: standardStyle, textAttributes: attrs) + } else { + return .init(text: subTitle.text, textAttributes: attrs) } } } + + +extension TitleLockupModel { + public var surface: Surface { inverted ? .dark : .light } +} + From 86b835fc60c846ba02ccde70e2bfa11424bccd3b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Dec 2023 15:43:40 -0600 Subject: [PATCH 03/17] fixed bug Signed-off-by: Matt Bruce --- .../LockUps/TitleLockupModel.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift index fb930bde..bac8ef34 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift @@ -88,13 +88,13 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco } public func eyebrowModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.EyebrowModel? { - guard let subTitle else { return nil } - let attrs = subTitle.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) - let style: TextStyle? = subTitle.fontStyle?.vdsTextStyle() + guard let eyebrow else { return nil } + let attrs = eyebrow.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) + let style: TextStyle? = eyebrow.fontStyle?.vdsTextStyle() if let style, let standardStyle = VDS.TitleLockup.OtherStandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: subTitle.text, isBold: style.isBold, standardStyle: standardStyle, textAttributes: attrs) + return .init(text: eyebrow.text, isBold: style.isBold, standardStyle: standardStyle, textAttributes: attrs, numberOfLines: eyebrow.numberOfLines ?? 0) } else { - return .init(text: subTitle.text, textAttributes: attrs) + return .init(text: eyebrow.text, textAttributes: attrs, numberOfLines: eyebrow.numberOfLines ?? 0) } } @@ -102,9 +102,9 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco let attrs = title.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) let style: TextStyle? = title.fontStyle?.vdsTextStyle() if let style, let standardStyle = VDS.TitleLockup.TitleStandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: title.text, textAttributes: attrs, isBold: style.isBold, standardStyle: standardStyle) + return .init(text: title.text, textAttributes: attrs, isBold: style.isBold, standardStyle: standardStyle, numberOfLines: title.numberOfLines ?? 0) } else { - return .init(text: title.text, textAttributes: attrs) + return .init(text: title.text, textAttributes: attrs, numberOfLines: title.numberOfLines ?? 0) } } @@ -113,9 +113,9 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco let attrs = subTitle.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) let style: TextStyle? = subTitle.fontStyle?.vdsTextStyle() if let style, let standardStyle = VDS.TitleLockup.OtherStandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: subTitle.text, standardStyle: standardStyle, textAttributes: attrs) + return .init(text: subTitle.text, standardStyle: standardStyle, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) } else { - return .init(text: subTitle.text, textAttributes: attrs) + return .init(text: subTitle.text, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) } } } From 2e937fd0de790339a9bc1f109fc2b6ccd7162bb3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Feb 2024 17:30:40 -0600 Subject: [PATCH 04/17] updated model Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift index 19284756..fe37a2c0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/LabelModel.swift @@ -5,7 +5,7 @@ // Created by Suresh, Kamlesh on 10/3/19. // Copyright © 2019 Suresh, Kamlesh. All rights reserved. // - +import VDS @objcMembers open class LabelModel: MoleculeModelProtocol { //-------------------------------------------------- @@ -30,6 +30,7 @@ public var numberOfLines: Int? public var shouldMaskRecordedView: Bool? = false public var accessibilityTraits: UIAccessibilityTraits? + public var inverted: Bool = false //-------------------------------------------------- // MARK: - Keys @@ -49,6 +50,7 @@ case attributes case html case hero + case inverted case makeWholeViewClickable case numberOfLines case shouldMaskRecordedView @@ -97,6 +99,7 @@ attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes) html = try typeContainer.decodeIfPresent(String.self, forKey: .html) hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) + inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) ?? false makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable) numberOfLines = try typeContainer.decodeIfPresent(Int.self, forKey: .numberOfLines) shouldMaskRecordedView = try typeContainer.decodeIfPresent(Bool.self, forKey: .shouldMaskRecordedView) ?? false @@ -123,9 +126,14 @@ try container.encodeModelsIfPresent(attributes, forKey: .attributes) try container.encodeIfPresent(html, forKey: .html) try container.encodeIfPresent(hero, forKey: .hero) + try container.encodeIfPresent(inverted, forKey: .inverted) try container.encodeIfPresent(makeWholeViewClickable, forKey: .makeWholeViewClickable) try container.encodeIfPresent(numberOfLines, forKey: .numberOfLines) try container.encodeIfPresent(shouldMaskRecordedView, forKey: .shouldMaskRecordedView) try container.encodeIfPresent(accessibilityTraits, forKey: .accessibilityTraits) } } + +extension LabelModel { + public var surface: Surface { inverted ? .dark : .light } +} From e3b335e6a6d9b3e78475e08253dc266e57513843 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Feb 2024 17:30:59 -0600 Subject: [PATCH 05/17] updated Styler Signed-off-by: Matt Bruce --- MVMCoreUI/Styles/Styler.swift | 59 ++--------------------------------- 1 file changed, 3 insertions(+), 56 deletions(-) diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift index 2daac86a..1f147241 100644 --- a/MVMCoreUI/Styles/Styler.swift +++ b/MVMCoreUI/Styles/Styler.swift @@ -57,59 +57,6 @@ open class Styler { case B2 // Maps to RegularBodySmall case B3 // Maps to RegularMicro - /// Returns the font size of the current enum case. - public func pointSize() -> CGFloat { - switch self { - case .RegularFeatureXLarge, - .BoldFeatureXLarge: - return 96 - case .RegularFeatureLarge, - .BoldFeatureLarge: - return 80 - case .RegularFeatureMedium, - .BoldFeatureMedium: - return 64 - case .RegularFeatureSmall, - .BoldFeatureSmall: - return 48 - case .RegularFeatureXSmall, - .BoldFeatureXSmall, - .RegularTitle2XLarge, - .BoldTitle2XLarge, - .Title2XLarge, - .H1: - return 40 - case .RegularTitleXLarge, - .BoldTitleXLarge, - .TitleXLarge, - .H32: - return 32 - case .BoldTitleLarge, - .RegularTitleLarge, - .H2: - return 24 - case .BoldTitleMedium, - .RegularTitleMedium, - .H3: - return 20 - case .RegularTitleSmall, - .BoldTitleSmall, - .BoldBodyLarge, - .RegularBodyLarge, - .B20: - return 16 - case .RegularBodyMedium, - .BoldBodyMedium: - return 14 - case .BoldBodySmall, .B1, - .RegularBodySmall, .B2: - return 12 - case .BoldMicro, - .RegularMicro, .B3: - return 11 - } - } - public func color() -> UIColor { switch self { case .B3: @@ -148,8 +95,8 @@ open class Styler { /// Returns the font based on the declared enum case. public func getFont(_ genericScaling: Bool = true) -> UIFont { - let size = genericScaling ? sizeFontGeneric(forCurrentDevice: pointSize()) : pointSize() - return MFStyler.getFontFor(size: size, isBold: isBold()) + let vdsStyle = vdsTextStyle() ?? .defaultStyle + return vdsStyle.font } /// Styles the provided label to the declared enum Font case. @@ -237,7 +184,7 @@ open class Styler { /// Creates the appropriate VZW font for a VDS style, scaling based on the scaleValue threshold passed in. @objc static func getFontFor(styleString: String, scaleValue: CGFloat) -> UIFont? { guard let font = Styler.Font(rawValue: styleString), - let size = Styler.Font(rawValue: styleString)?.pointSize(), + let size = font.vdsTextStyle()?.pointSize, let newSize = Styler.sizeObjectGeneric(forCurrentDevice: size)?.getValueBased(onSize: scaleValue) else { return nil } return getFontFor(size: newSize, isBold: font.isBold()) } From 29f43b7861c14f65c84b2e375d580da9f590ca34 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Feb 2024 17:31:13 -0600 Subject: [PATCH 06/17] added helper for TextStyle extension Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift index 09fc5dcd..996274e3 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift @@ -29,3 +29,17 @@ extension Styler.Font { } } +extension VDS.Font { + internal static func from(fontName: String) -> Self? { + Self.allCases.filter({$0.fontName == fontName }).first + } +} + +extension VDS.TextStyle { + internal static func style(from font: VDS.Font, pointSize: CGFloat) -> TextStyle? { + guard let first = allCases.filter({$0.fontFace == font && $0.pointSize == pointSize}).first else { + return TextStyle(rawValue: "Custom-TextStyle", fontFace: font, pointSize: pointSize) + } + return first + } +} From ddb15e72f3f9d846e0f43910aefc79c865f75ca2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Feb 2024 17:32:06 -0600 Subject: [PATCH 07/17] refactored label for first cut, pretty big changes though.... Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/Label/Label.swift | 781 +++--------------- 1 file changed, 117 insertions(+), 664 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 59611756..609c5e0f 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -8,15 +8,18 @@ // import MVMCore +import VDS public typealias ActionBlock = () -> () -@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ViewMaskingProtocol { - +@objcMembers open class Label: VDS.Label, VDSMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ViewMaskingProtocol { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ + open var viewModel: LabelModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? public var makeWholeViewClickable = false @@ -30,52 +33,15 @@ public typealias ActionBlock = () -> () /// A specific text index to use as a unique marker. public var hero: Int? - // Used for scaling the font in updateView. - private var originalAttributedString: NSAttributedString? - - public var hasText: Bool { - guard let text = text, let attributedText = attributedText else { return false } - return !text.isEmpty || !attributedText.string.isEmpty - } - public var getRange: NSRange { NSRange(location: 0, length: text?.count ?? 0) } public var shouldMaskWhileRecording: Bool = false - public var model: MoleculeModelProtocol? - //------------------------------------------------------ - // MARK: - Multi-Action Text - //------------------------------------------------------ - - /// Data store of the tappable ranges of the text. - public var clauses: [ActionableClause] = [] { - didSet { - isUserInteractionEnabled = !clauses.isEmpty - if clauses.count > 1 { - clauses.sort { first, second in - return first.range.location < second.range.location - } - } - } - } - - /// Used for tappable links in the text. - public struct ActionableClause { - public var range: NSRange - public var actionBlock: ActionBlock - public var accessibilityID: Int = 0 - - public func performAction() { - actionBlock() - } - - public init(range: NSRange, actionBlock: @escaping ActionBlock, accessibilityID: Int = 0) { - self.range = range - self.actionBlock = actionBlock - self.accessibilityID = accessibilityID - } + public var hasText: Bool { + guard let text = text, let attributedText = attributedText else { return false } + return !text.isEmpty || !attributedText.string.isEmpty } //------------------------------------------------------ @@ -84,54 +50,32 @@ public typealias ActionBlock = () -> () /// Sets the clauses array to empty. @objc public func setEmptyClauses() { - clauses = [] } //------------------------------------------------------ // MARK: - Initialization //------------------------------------------------------ - @objc public func setupView() { - backgroundColor = .clear - numberOfLines = 0 - lineBreakMode = .byWordWrapping - translatesAutoresizingMaskIntoConstraints = false - clauses = [] - accessibilityCustomActions = [] - accessibilityTraits = .staticText - - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped)) - tapGesture.numberOfTapsRequired = 1 - addGestureRecognizer(tapGesture) - } - - @objc public init() { - super.init(frame: .zero) - setupView() + @objc public required init() { + super.init() } @objc required public init?(coder: NSCoder) { super.init(coder: coder) - setupView() } @objc override public init(frame: CGRect) { super.init(frame: frame) - setupView() } public init(fontStyle: Styler.Font, _ scale: Bool = true) { super.init(frame: .zero) - setupView() - - font = fontStyle.getFont(false) - textColor = fontStyle.color() - setScale(scale) + guard let style = fontStyle.vdsTextStyle() else { return } + textStyle = style } @objc convenience public init(standardFontSize size: CGFloat) { self.init() - standardFontSize = size } /// Convenience to init Label with a link comprised of range, actionMap and delegateObject @@ -145,7 +89,6 @@ public typealias ActionBlock = () -> () required public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.init(frame: .zero) - setupView() styleB2(true) set(with: model, delegateObject, additionalData) } @@ -236,12 +179,6 @@ public typealias ActionBlock = () -> () } } - enum LabelAlignment: String { - case center - case right - case left - } - @objc public func resetAttributeStyle() { /* * This is to address a reuse issue with iOS 13 and up. @@ -250,294 +187,104 @@ public typealias ActionBlock = () -> () * appropriately called. * Only other reference found of issue: https://www.thetopsites.net/article/58142205.shtml */ - if let attributedText = attributedText, let text = text, !text.isEmpty { - let attributedString = NSMutableAttributedString(string: text) - let range = NSRange(location: 0, length: text.count) - for attribute in attributedText.attributes(at: 0, effectiveRange: nil) { - if attribute.key == .underlineStyle { - attributedString.addAttribute(.underlineStyle, value: 0, range: range) - } - if attribute.key == .strikethroughStyle { - attributedString.addAttribute(.strikethroughStyle, value: 0, range: range) - } + if let text = text, !text.isEmpty { + + //create the primary string + let mutableText = NSMutableAttributedString.mutableText(for: text, + textStyle: textStyle, + useScaledFont: useScaledFont, + textColor: textColorConfiguration.getColor(self), + alignment: textAlignment, + lineBreakMode: lineBreakMode) + + if let attributes = attributes { + mutableText.apply(attributes: attributes) } - self.attributedText = attributedString + self.attributedText = mutableText } } - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject? = nil, _ additionalData: [AnyHashable: Any]? = nil) { + public func viewModelDidUpdate() { + shouldMaskWhileRecording = viewModel.shouldMaskRecordedView ?? false + text = viewModel.text + hero = viewModel.hero + Label.setLabel(self, withHTML: viewModel.html) + textAlignment = viewModel.textAlignment ?? .left + surface = viewModel.surface - clauses = [] - text = nil - attributedText = nil - originalAttributedString = nil - shouldMaskWhileRecording = model.shouldMaskRecordedView ?? false - - guard let labelModel = model as? LabelModel else { return } - - text = labelModel.text - if let accessibilityTraits = labelModel.accessibilityTraits { - self.accessibilityTraits = accessibilityTraits - } - - resetAttributeStyle() - - hero = labelModel.hero - Label.setLabel(self, withHTML: labelModel.html) - isAccessibilityElement = hasText - - switch labelModel.textAlignment { - case .center: - textAlignment = .center - - case .right: - textAlignment = .right - - default: - textAlignment = .left - } - - makeWholeViewClickable = labelModel.makeWholeViewClickable ?? false - if let backgroundColor = labelModel.backgroundColor { + makeWholeViewClickable = viewModel.makeWholeViewClickable ?? false + if let backgroundColor = viewModel.backgroundColor { self.backgroundColor = backgroundColor.uiColor } - if let accessibilityText = labelModel.accessibilityText { - accessibilityLabel = accessibilityText - } - - if let fontStyle = labelModel.fontStyle { - fontStyle.styleLabel(self, genericScaling: false) - standardFontSize = font.pointSize - } else { - let fontSize = labelModel.fontSize - if let fontSize = fontSize { + if let style = viewModel.fontStyle?.vdsTextStyle() { + font = style.font + textStyle = style + } else if let fontName = viewModel.fontName { + // there is a TextStyle.defaultStyle + let fontSize = viewModel.fontSize + if let fontSize { standardFontSize = fontSize } - if let fontName = labelModel.fontName { - font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize) - } else if let fontSize = fontSize { - font = font.updateSize(fontSize) + if let customStyle = style(for: fontName, pointSize: fontSize ?? standardFontSize), customStyle != textStyle { + font = customStyle.font + textStyle = customStyle } } - if let color = labelModel.textColor { - textColor = color.uiColor + if let color = viewModel.textColor { + textColorConfiguration = SurfaceColorConfiguration(color.uiColor, color.uiColor).eraseToAnyColorable() } - if let lines = labelModel.numberOfLines { - numberOfLines = lines + if let lines = viewModel.numberOfLines { + numberOfLines = lines } - - if let attributes = labelModel.attributes, let labelText = text { - let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.updateSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor]) - - for attribute in attributes { - guard let range = validateAttribute(range: NSRange(location: attribute.location, length: attribute.length) , in: attributedString, type: attribute.type) - else { continue } - - switch attribute { - case let underlineAtt as LabelAttributeUnderlineModel: - attributedString.addAttribute(.underlineStyle, value: underlineAtt.underlineValue.rawValue, range: range) - if let underlineColor = underlineAtt.color?.uiColor { - attributedString.addAttribute(.underlineColor, value: underlineColor, range: range) - } - - case _ as LabelAttributeStrikeThroughModel: - attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) - attributedString.addAttribute(.baselineOffset, value: 0, range: range) - - case let colorAtt as LabelAttributeColorModel: - if let colorHex = colorAtt.textColor { - attributedString.removeAttribute(.foregroundColor, range: range) - attributedString.addAttribute(.foregroundColor, value: colorHex.uiColor, range: range) - } - - case let imageAtt as LabelAttributeImageModel: - var fontSize = font.pointSize - if let attributeSize = imageAtt.size { - fontSize = attributeSize - } - let imageName = imageAtt.name ?? "externalLink" - let imageAttachment: NSTextAttachment - - if let url = imageAtt.URL { - imageAttachment = Label.getTextAttachmentFrom(url: url, dimension: fontSize, label: self) - } else { - imageAttachment = Label.getTextAttachmentImage(name: imageName, dimension: fontSize) - } - - // Confirm that the intended image location is within range. - if 0...labelText.count ~= imageAtt.location { - let mutableString = NSMutableAttributedString() - mutableString.append(NSAttributedString(attachment: imageAttachment)) - attributedString.insert(mutableString, at: imageAtt.location) - } - - case let fontAtt as LabelAttributeFontModel: - if let fontStyle = fontAtt.style { - attributedString.removeAttribute(.font, range: range) - attributedString.removeAttribute(.foregroundColor, range: range) - attributedString.addAttribute(.font, value: fontStyle.getFont(), range: range) - attributedString.addAttribute(.foregroundColor, value: fontStyle.color(), range: range) - } else { - let fontSize = fontAtt.size - var font: UIFont? - - if let fontName = fontAtt.name { - font = MFFonts.mfFont(withName: fontName, size: fontSize ?? self.font.pointSize) - } else if let fontSize = fontSize { - font = self.font.updateSize(fontSize) - } - if let font = font { - attributedString.removeAttribute(.font, range: range) - attributedString.addAttribute(.font, value: font, range: range) - } - } - case let actionAtt as LabelAttributeActionModel: - addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) { - MVMCoreUIActionHandler.performActionUnstructured(with: actionAtt.action, sourceModel: model, additionalData: additionalData, delegateObject: delegateObject) - } - addActionAttributes(range: range, string: attributedString) - - default: - continue - } - } - - attributedText = attributedString - originalAttributedString = attributedText + + if let attributeModels = viewModel.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) { + attributes = attributeModels } - self.model = labelModel + + } + + /// See if the font that is currently set matches a VDS Font and if so grab the matching TextStyle or create custom TextStyle that + /// that the Label will use moving forward. + private func checkforFontChange() { + guard let customStyle = style(for: font.fontName, pointSize: font.pointSize), customStyle != textStyle + else { return } + textStyle = customStyle } + private func style(for fontName: String, pointSize: CGFloat) -> TextStyle? { + guard let vdsFont = Font.from(fontName: fontName), + let customStyle = TextStyle.style(from: vdsFont, pointSize: pointSize) + else { return nil } + return customStyle + } + + open override func updateView() { + checkforFontChange() + super.updateView() + } + + open override func updateAccessibility() { + super.updateAccessibility() + if let accessibilityTraits = viewModel?.accessibilityTraits { + self.accessibilityTraits = accessibilityTraits + } + + if let accessibilityText = viewModel?.accessibilityText { + accessibilityLabel = accessibilityText + } + } + + public func updateView(_ size: CGFloat) { } + @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let label = label else { return } - - // Some properties can only be set on Label. - // Label fonts should not be scaled because it will be scaled in updateView. - let mvmLabel = label as? Label - - label.text = json?.optionalStringForKey(KeyText) - - setLabel(label, withHTML: json?.optionalStringForKey("html")) - - if let alignment = json?.optionalStringForKey("textAlignment") { - switch alignment { - case "center": - label.textAlignment = .center - case "right": - label.textAlignment = .right - default: - label.textAlignment = .left - } - } - - mvmLabel?.makeWholeViewClickable = json?.boolForKey("makeWholeViewClickable") ?? false - - if let backgroundColorHex = json?.optionalStringForKey(KeyBackgroundColor), !backgroundColorHex.isEmpty { - label.backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) - } - - label.accessibilityLabel = json?.optionalStringForKey("accessibilityText") - - if let fontStyle = json?.optionalStringForKey("fontStyle") { - MFStyler.style(label: label, styleString: fontStyle, genericScaling: mvmLabel == nil) - mvmLabel?.standardFontSize = label.font.pointSize - } else { - let fontSize = json?["fontSize"] as? CGFloat - if let fontSize = fontSize { - mvmLabel?.standardFontSize = fontSize - } - - if let fontName = json?.optionalStringForKey("fontName") { - label.font = MFFonts.mfFont(withName: fontName, size: fontSize ?? mvmLabel?.standardFontSize ?? label.font.pointSize) - } else if let fontSize = fontSize { - label.font = label.font.updateSize(fontSize) - } - } - - if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty { - label.textColor = UIColor.mfGet(forHex: textColorHex) - } - - if let attributes = json?.optionalArrayForKey("attributes"), let labelText = label.text { - let attributedString = NSMutableAttributedString(string: labelText, - attributes: [NSAttributedString.Key.font: mvmLabel?.font.updateSize(mvmLabel!.standardFontSize) ?? label.font as UIFont, - NSAttributedString.Key.foregroundColor: label.textColor as UIColor]) - for case let attribute as [String: Any] in attributes { - guard let attributeType = attribute.optionalStringForKey(KeyType), - let location = attribute["location"] as? Int, - let length = attribute["length"] as? Int, - let range = validateAttribute(range: NSRange(location: location, length: length), in: attributedString, type: attributeType) - else { continue } - - switch attributeType { - case "underline": - attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) - - case "strikethrough": - attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) - attributedString.addAttribute(.baselineOffset, value: 0, range: range) - - case "color": - if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty { - attributedString.removeAttribute(.foregroundColor, range: range) - attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) - } - case "image": - let fontSize = attribute["size"] as? CGFloat ?? label.font.pointSize - let imageName = attribute["name"] as? String ?? "externalLink" - let imageURL = attribute["URL"] as? String - let imageAttachment: NSTextAttachment - - if let url = imageURL, let label = label as? Label { - imageAttachment = Label.getTextAttachmentFrom(url: url, dimension: fontSize, label: label) - } else { - imageAttachment = Label.getTextAttachmentImage(name: imageName, dimension: fontSize) - } - - let mutableString = NSMutableAttributedString() - mutableString.append(NSAttributedString(attachment: imageAttachment)) - attributedString.insert(mutableString, at: location) - - case "font": - if let fontStyle = attribute.optionalStringForKey("style") { - let styles = MFStyler.getAttributedString(for: "0", styleString: fontStyle, genericScaling: mvmLabel == nil) - attributedString.removeAttribute(.font, range: range) - attributedString.removeAttribute(.foregroundColor, range: range) - attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range) - } else { - let fontSize = attribute["size"] as? CGFloat - var font: UIFont? - - if let fontName = attribute.optionalStringForKey("name") { - font = MFFonts.mfFont(withName: fontName, size: fontSize ?? mvmLabel?.standardFontSize ?? label.font.pointSize) - } else if let fontSize = fontSize { - font = label.font.updateSize(fontSize) - } - - if let font = font { - attributedString.removeAttribute(.font, range: range) - attributedString.addAttribute(.font, value: font, range: range) - } - } - case "action": - guard let actionLabel = label as? Label else { continue } - - actionLabel.addActionAttributes(range: range, string: attributedString) - if let actionBlock = actionLabel.createActionBlockFor(actionMap: attribute, additionalData: additionalData, delegateObject: delegate) { - actionLabel.appendActionableClause(range: range, actionBlock: actionBlock) - } - - default: - continue - } - } - label.attributedText = attributedString - mvmLabel?.originalAttributedString = attributedString - } + guard let label = label as? Label, + let json = json as? [String: Any], + let labelModel = try? LabelModel.decode(jsonDict: json) else { return } + label.set(with: labelModel, delegate as? MVMCoreUIDelegateObject, additionalData) } //------------------------------------------------------ @@ -545,206 +292,53 @@ public typealias ActionBlock = () -> () //------------------------------------------------------ public func setFontStyle(_ fontStyle: Styler.Font, _ scale: Bool = true) { - fontStyle.styleLabel(self, genericScaling: false) - setScale(scale) + style(for: fontStyle) } //------------------------------------------------------ // MARK: - 2.0 Styling Methods //------------------------------------------------------ + private func style(for legacy: Styler.Font) { + guard let style = legacy.vdsTextStyle() else { return } + textStyle = style + } @objc public func styleH1(_ scale: Bool) { - MFStyler.styleLabelH1(self, genericScaling: false) - setScale(scale) + style(for: .H1) } @objc public func styleH2(_ scale: Bool) { - MFStyler.styleLabelH2(self, genericScaling: false) - setScale(scale) + style(for: .H2) } @objc public func styleH3(_ scale: Bool) { - MFStyler.styleLabelH3(self, genericScaling: false) - setScale(scale) + style(for: .H3) } @objc public func styleH32(_ scale: Bool) { - MFStyler.styleLabelH32(self, genericScaling: false) - setScale(scale) + style(for: .H32) } @objc public func styleB1(_ scale: Bool) { - MFStyler.styleLabelB1(self, genericScaling: false) - setScale(scale) + style(for: .B1) } @objc public func styleB2(_ scale: Bool) { - MFStyler.styleLabelB2(self, genericScaling: false) - setScale(scale) + style(for: .B2) } @objc public func styleB3(_ scale: Bool) { - MFStyler.styleLabelB3(self, genericScaling: false) - setScale(scale) + style(for: .B3) } @objc public func styleB20(_ scale: Bool) { - MFStyler.styleLabelB20(self, genericScaling: false) - setScale(scale) + style(for: .B20) } - /// Will remove the values contained in attributedText. - func clearAttributes() { - guard let labelText = text, !labelText.isEmpty else { return } - guard let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: labelText.count)) - else { return } - - let attributedString = NSMutableAttributedString(string: labelText) - - for attribute in attributes { - attributedString.removeAttribute(attribute.key, range: NSRange(location: 0, length: labelText.count)) - } - - attributedText = attributedString - } +} - - @objc public func updateView(_ size: CGFloat) { - scaleSize = size as NSNumber - - if let originalAttributedString = originalAttributedString { - let attributedString = NSMutableAttributedString(attributedString: originalAttributedString) - attributedString.removeAttribute(.font, range: NSRange(location: 0, length: attributedString.length)) - - // Loop the original attributed string, resize the fonts. - originalAttributedString.enumerateAttribute(.font, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in - - if let fontObj = value as? UIFont, let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: fontObj.pointSize)?.getValueBased(onSize: size) { - attributedString.addAttribute(.font, value: fontObj.updateSize(stylerSize) as Any, range: range) - } - } - - // Loop the original attributed string, resize the image attachments. - originalAttributedString.enumerateAttribute(.attachment, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in - if let attachment = value as? NSTextAttachment, - let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: attachment.bounds.width)?.getValueBased(onSize: size) { - - let dimension = round(stylerSize) - attachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) - } - } - - attributedText = attributedString - } else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject = sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) { - font = font.updateSize(sizeObject.getValueBased(onSize: size)) - } - } - - @objc public func setFont(_ font: UIFont, scale: Bool) { - self.font = font - setScale(scale) - } - - @objc public func setScale(_ scale: Bool) { - if scale { - standardFontSize = font.pointSize - if let floatScale = scaleSize?.floatValue { - updateView(CGFloat(floatScale)) - } else { - updateView(MVMCoreUIUtility.getWidth()) - } - } else { - standardFontSize = 0 - } - } - - /** - Appends an external link image to the end of the attributed string. - Will provide one whitespace to the left of the icon; adds 2 chars to the end of the string. - */ - @objc public func appendExternalLinkIcon() { - - guard let attributedText = attributedText else { return } - - let mutableString = NSMutableAttributedString(attributedString: attributedText) - - mutableString.append(NSAttributedString(string: " ")) - mutableString.append(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize))) - self.attributedText = mutableString - } - - /** - Insert external link icon anywhere within text of Label. - - - Note: Each icon insertion adds 1 additional characters to the overall text length. - Therefore, you **MUST** insert icons and links in the order they would appear. - - parameter index: Location within the associated text to insert an external Link Icon - */ - public func insertExternalLinkIcon(at index: Int) { - - guard let attributedText = attributedText, index <= attributedText.string.count && index >= 0 else { return } - - let mutableString = NSMutableAttributedString(attributedString: attributedText) - mutableString.insert(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)), at: index) - - self.attributedText = mutableString - } - - /* - Retrieves an NSTextAttachment for NSAttributedString that is prepped to be inserted with the text. - - - parameter name: The Asset name of the image. DEFAULT: "externalLink" - - parameter dimension: length of the height and width of the image. Will be 80% the passed magnitude. - */ - static func getTextAttachmentImage(name: String = "externalLink", dimension: CGFloat) -> NSTextAttachment { - - let imageAttachment = NSTextAttachment() - imageAttachment.image = MVMCoreCache.shared()?.getImageFromRegisteredBundles(name) - imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) - - return imageAttachment - } - - static func getTextAttachmentFrom(url: String, dimension: CGFloat, label: Label) -> NSTextAttachment { - - let imageAttachment = NSTextAttachment() - imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) - - DispatchQueue.global(qos: .default).async { - MVMCoreCache.shared()?.getImage(url, useWidth: false, widthForS7: 0, useHeight: false, heightForS7: 0, localFallbackImageName: nil) { image, data, _ in - - DispatchQueue.main.sync { - imageAttachment.image = image - label.setNeedsDisplay() - } - } - } - return imageAttachment - } - - /// Call to detect in the attributedText contains an NSTextAttachment. - func containsTextAttachment() -> Bool { - - guard let attributedText = attributedText else { return false } - - var containsAttachment = false - - attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, stop in - if value is NSTextAttachment { - containsAttachment = true - return - } - } - - return containsAttachment - } - - func appendActionableClause(range: NSRange, actionBlock: @escaping ActionBlock) { - - accessibilityTraits = .button - let accessibleAction = customAccessibilityAction(range: range) - clauses.append(ActionableClause(range: range, actionBlock: actionBlock, accessibilityID: accessibleAction?.hash ?? -1)) - } +// Mark: - Old Helpers +extension Label { public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { @@ -789,25 +383,11 @@ public typealias ActionBlock = () -> () return (textContainer, layoutManager, textStorage) } - } // MARK: - Atomization extension Label { - - public func reset() { - text = nil - attributedText = nil - hero = nil - textAlignment = .left - originalAttributedString = nil - styleB2(true) - accessibilityCustomActions = [] - clauses = [] - accessibilityTraits = .staticText - numberOfLines = 0 - } - + public func needsToBeConstrained() -> Bool { true } public func horizontalAlignment() -> UIStackView.Alignment { .leading } @@ -817,19 +397,16 @@ extension Label { // MARK: - Multi-Link Functionality extension Label { - - /// Applied to existing text. Removes underlines of tappable links and assoated actionable clauses. - @objc public func clearActionableClauses() { - guard let attributedText = attributedText else { return } - let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) - - clauses.forEach { clause in - mutableAttributedString.removeAttribute(NSAttributedString.Key.underlineStyle, range: clause.range) - } - - self.attributedText = mutableAttributedString - accessibilityElements = [] - clauses = [] + + /// Underlines the tappable region and stores the tap logic for interation. + private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { + var textLink = ActionLabelAttribute(location: range.location, length: range.length) + textLink.subscriber = textLink + .action + .sink { _ in + actionBlock() + } + attributes?.append(textLink) } public func createActionBlockFor(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? { @@ -842,24 +419,6 @@ extension Label { } } - private func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) { - - guard let string = string, - let range = validateAttribute(range: range, in: string) - else { return } - - string.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range) - } - - fileprivate func setActionAttributes(range: NSRange) { - - guard let attributedText = attributedText else { return } - - let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) - addActionAttributes(range: range, string: mutableAttributedString) - self.attributedText = mutableAttributedString - } - /** Provides an actionable range of text. @@ -898,113 +457,6 @@ extension Label { setTextLinkState(range: getRange, actionBlock: actionBlock) } - - /// Underlines the tappable region and stores the tap logic for interation. - private func setTextLinkState(range: NSRange, actionBlock: @escaping ActionBlock) { - - setActionAttributes(range: range) - appendActionableClause(range: range, actionBlock: actionBlock) - } - - @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { - - for clause in clauses { - // This determines if we tapped on the desired range of text. - if gesture.didTapAttributedTextInLabel(self, inRange: clause.range) { - clause.performAction() - return - } - } - } -} - -// MARK: - -extension UITapGestureRecognizer { - - func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { - - // There would only ever be one clause to act on. - if label.makeWholeViewClickable { - return true - } - - guard let abstractContainer = label.abstractTextContainer() else { return false } - let textContainer = abstractContainer.0 - let layoutManager = abstractContainer.1 - - let tapLocation = location(in: label) - let indexOfGlyph = layoutManager.glyphIndex(for: tapLocation, in: textContainer) - let intrinsicWidth = label.intrinsicContentSize.width - - // Assert that tapped occured within acceptable bounds based on alignment. - switch label.textAlignment { - case .right: - if tapLocation.x < label.bounds.width - intrinsicWidth { - return false - } - case .center: - let halfBounds = label.bounds.width / 2 - let halfIntrinsicWidth = intrinsicWidth / 2 - - if tapLocation.x > halfBounds + halfIntrinsicWidth { - return false - } else if tapLocation.x < halfBounds - halfIntrinsicWidth { - return false - } - default: // Left align - if tapLocation.x > intrinsicWidth { - return false - } - } - - // Affirms that the tap occured in the desired rect of provided by the target range. - return layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer).contains(tapLocation) && NSLocationInRange(indexOfGlyph, targetRange) - } -} - -// MARK: - Accessibility -extension Label { - - func customAccessibilityAction(range: NSRange) -> UIAccessibilityCustomAction? { - - guard let text = text else { return nil } - - if accessibilityHint == nil { - accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint") - } - - let actionText = NSString(string: text).substring(with: range) - let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:))) - accessibilityCustomActions?.append(accessibleAction) - - return accessibleAction - } - - @objc public func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) { - - for clause in clauses { - if action.hash == clause.accessibilityID { - clause.performAction() - return - } - } - } - - open override func accessibilityActivate() -> Bool { - - guard let accessibleActions = accessibilityCustomActions else { return false } - - for clause in clauses { - for action in accessibleActions { - if action.hash == clause.accessibilityID { - clause.performAction() - return true - } - } - } - - return false - } } //------------------------------------------------------ @@ -1025,3 +477,4 @@ func validateAttribute(range: NSRange, in string: NSAttributedString, type: Stri return range } + From 28c15efba50694618046640ae9128af063715565 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 12 Feb 2024 17:32:34 -0600 Subject: [PATCH 08/17] refactored atoms with style changes Signed-off-by: Matt Bruce --- .../Atoms/FormFields/TextFields/EntryField.swift | 14 +++++++------- MVMCoreUI/Atomic/Atoms/Views/Label/FormLabel.swift | 6 +----- .../LockUps/LockupsPlanSMLXL.swift | 7 ++----- .../LeftRightViews/ActionDetailWithImage.swift | 4 ++-- .../ToggleMolecules/LabelToggle.swift | 2 +- .../HeadlineBodyButton.swift | 4 ++-- 6 files changed, 15 insertions(+), 22 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift index c583638c..bc3e9617 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryField.swift @@ -19,6 +19,7 @@ import UIKit //-------------------------------------------------- public private(set) var titleLabel: FormLabel = { let label = FormLabel() + label.setFontStyle(.RegularMicro) label.setContentCompressionResistancePriority(.required, for: .vertical) return label }() @@ -28,6 +29,7 @@ import UIKit /// Provides contextual information on the TextField. public private(set) var feedbackLabel: FormLabel = { let label = FormLabel() + label.setFontStyle(.RegularMicro) label.setContentCompressionResistancePriority(.required, for: .vertical) return label }() @@ -276,15 +278,13 @@ import UIKit backgroundColor = .clear isAccessibilityElement = false - titleLabel.font = Styler.Font.RegularMicro.getFont() - titleLabel.textColor = .mvmBlack - feedbackLabel.font = Styler.Font.RegularMicro.getFont() - feedbackLabel.textColor = .mvmBlack - errorLabel.font = Styler.Font.RegularMicro.getFont() - errorLabel.textColor = .mvmBlack + titleLabel.setFontStyle(.RegularMicro) + feedbackLabel.setFontStyle(.RegularMicro) + errorLabel.setFontStyle(.RegularMicro) + titleLabel.text = nil + feedbackLabel.text = nil errorLabel.text = nil entryFieldContainer.disableAllBorders = false - feedbackLabel.text = nil entryFieldContainer.reset() entryFieldModel?.updateUI = nil } diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/FormLabel.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/FormLabel.swift index 08b7267e..a7755928 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/FormLabel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/FormLabel.swift @@ -9,11 +9,7 @@ import Foundation /// Subclass of label that helps with different states -public class FormLabel: Label { - //properties used in setting label - private var delegateObject: MVMCoreUIDelegateObject? - private var additionalData: [AnyHashable: Any]? - +public class FormLabel: Label { //models that drive the label UI private var formModel: FormLabelModel! diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/LockupsPlanSMLXL.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/LockupsPlanSMLXL.swift index c0d4c44f..5e990303 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/LockupsPlanSMLXL.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/LockupsPlanSMLXL.swift @@ -12,7 +12,7 @@ import Foundation //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- - public let planLabel = Label() + public let planLabel = Label(fontStyle: .BoldFeatureXLarge) public let headline = Label(fontStyle: .BoldTitleLarge) public let subHeadline = Label(fontStyle: .RegularTitleLarge) public let body = Label(fontStyle: .RegularBodySmall) @@ -33,8 +33,6 @@ import Foundation //------------------------------------------------------- open override func setupView() { super.setupView() - planLabel.font = MFStyler.getMVA3FontSize(96, bold: true) - planLabel.standardFontSize = 96 addSubview(stack) planLabel.setContentCompressionResistancePriority(.required, for: .horizontal) planLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) @@ -94,8 +92,7 @@ import Foundation open override func reset() { super.reset() stack.reset() - planLabel.font = MFStyler.getMVA3FontSize(96, bold: true) - planLabel.standardFontSize = 96 + planLabel.setFontStyle(.BoldFeatureXLarge) headline.setFontStyle(.BoldTitleLarge) subHeadline.setFontStyle(.RegularTitleLarge) body.setFontStyle(.RegularBodySmall) diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift index 74543d21..d3a346fc 100644 --- a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ActionDetailWithImage.swift @@ -76,8 +76,8 @@ import UIKit private func setDefaultState() { - headlineBodyButton.headlineBody.headlineLabel.font = MFStyler.fontBoldTitleMedium() - headlineBodyButton.headlineBody.messageLabel.font = MFStyler.fontRegularMicro() + headlineBodyButton.headlineBody.headlineLabel.setFontStyle(.BoldTitleMedium) + headlineBodyButton.headlineBody.messageLabel.setFontStyle(.RegularMicro) imageLoader.imageView.contentMode = .scaleAspectFit imageLoader.addSizeConstraintsForAspectRatio = true buttonHeaderPadding = PaddingTwo diff --git a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift index 5611984f..98fbb779 100644 --- a/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift +++ b/MVMCoreUI/Atomic/Molecules/LeftRightViews/ToggleMolecules/LabelToggle.swift @@ -14,7 +14,7 @@ import UIKit // MARK: - Properties //-------------------------------------------------- - public let label = Label(fontStyle: .BoldBodySmall) + public let label = Label().with { $0.font = Styler.Font.BoldFeatureXLarge.getFont() } public let toggle = Toggle() //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift index d7fced36..893b73d8 100644 --- a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/HeadlineBodyButton.swift @@ -62,8 +62,8 @@ private func defaultState() { - headlineBody.headlineLabel.font = Styler.Font.BoldTitleMedium.getFont() - headlineBody.messageLabel.font = Styler.Font.RegularMicro.getFont() + headlineBody.headlineLabel.setFontStyle(.BoldTitleMedium) + headlineBody.messageLabel.setFontStyle(.RegularMicro) button.use = .secondary button.isHidden = false buttonHeadlinePadding = PaddingTwo From 9e6b2a2bd67e052f9ee7b372b3975bbd51c504ee Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 13 Feb 2024 09:25:27 -0600 Subject: [PATCH 09/17] created a mapping method for the old values Signed-off-by: Matt Bruce --- .../Atomic/Extensions/VDS-TextStyle.swift | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift index 996274e3..f1fc1794 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift @@ -12,7 +12,7 @@ import VDS extension Styler.Font { //Converts Legacy Font to a VDS.TextStyle public func vdsTextStyle() -> VDS.TextStyle? { - let updatedRaw = rawValue.replacingOccurrences(of: "Regular", with: "") + let updatedRaw = mapped().rawValue.replacingOccurrences(of: "Regular", with: "") let newRaw = updatedRaw.prefix(1).lowercased() + updatedRaw.dropFirst() guard let style = VDS.TextStyle(rawValue: newRaw) else { return nil } return style @@ -27,6 +27,24 @@ extension Styler.Font { } return found } + + private func mapped() -> Styler.Font { + var mapped: Styler.Font = self + switch self { + case .Title2XLarge: mapped = .RegularTitle2XLarge + case .TitleXLarge: mapped = .RegularTitleXLarge + case .H1: mapped = .RegularTitle2XLarge + case .H32: mapped = .RegularTitleXLarge + case .H2: mapped = .RegularTitleLarge + case .B20: mapped = .RegularBodyLarge + case .H3: mapped = .BoldTitleMedium + case .B1: mapped = .BoldBodySmall + case .B2: mapped = .RegularBodySmall + case .B3: mapped = .RegularMicro + default: break + } + return mapped + } } extension VDS.Font { From f577f1a4193910ef17b503733c31258767a91658 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 13 Feb 2024 10:26:18 -0600 Subject: [PATCH 10/17] added back setFont/setScale Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/Label/Label.swift | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 609c5e0f..b6ecfb95 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -70,12 +70,12 @@ public typealias ActionBlock = () -> () public init(fontStyle: Styler.Font, _ scale: Bool = true) { super.init(frame: .zero) - guard let style = fontStyle.vdsTextStyle() else { return } - textStyle = style + setFontStyle(fontStyle, scale) } @objc convenience public init(standardFontSize size: CGFloat) { self.init() + standardFontSize = size } /// Convenience to init Label with a link comprised of range, actionMap and delegateObject @@ -280,6 +280,24 @@ public typealias ActionBlock = () -> () public func updateView(_ size: CGFloat) { } + @objc public func setFont(_ font: UIFont, scale: Bool) { + self.font = font + setScale(scale) + } + + @objc public func setScale(_ scale: Bool) { + if scale { + standardFontSize = font.pointSize + if let floatScale = scaleSize?.floatValue { + updateView(CGFloat(floatScale)) + } else { + updateView(MVMCoreUIUtility.getWidth()) + } + } else { + standardFontSize = 0 + } + } + @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { guard let label = label as? Label, let json = json as? [String: Any], @@ -292,47 +310,44 @@ public typealias ActionBlock = () -> () //------------------------------------------------------ public func setFontStyle(_ fontStyle: Styler.Font, _ scale: Bool = true) { - style(for: fontStyle) + guard let style = fontStyle.vdsTextStyle() else { return } + textStyle = style + setScale(scale) } //------------------------------------------------------ // MARK: - 2.0 Styling Methods //------------------------------------------------------ - private func style(for legacy: Styler.Font) { - guard let style = legacy.vdsTextStyle() else { return } - textStyle = style - } - @objc public func styleH1(_ scale: Bool) { - style(for: .H1) + setFontStyle(.H1, scale) } @objc public func styleH2(_ scale: Bool) { - style(for: .H2) + setFontStyle(.H2, scale) } @objc public func styleH3(_ scale: Bool) { - style(for: .H3) + setFontStyle(.H3, scale) } @objc public func styleH32(_ scale: Bool) { - style(for: .H32) + setFontStyle(.H32, scale) } @objc public func styleB1(_ scale: Bool) { - style(for: .B1) + setFontStyle(.B1, scale) } @objc public func styleB2(_ scale: Bool) { - style(for: .B2) + setFontStyle(.B2, scale) } @objc public func styleB3(_ scale: Bool) { - style(for: .B3) + setFontStyle(.B3, scale) } @objc public func styleB20(_ scale: Bool) { - style(for: .B20) + setFontStyle(.B20, scale) } } From 6718e151338555a644d6f20f3b428c168a3c7707 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 14 Feb 2024 15:34:41 -0600 Subject: [PATCH 11/17] added extra methods used by MF Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/Label/Label.swift | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index b6ecfb95..6d33d8ce 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -278,14 +278,18 @@ public typealias ActionBlock = () -> () } } - public func updateView(_ size: CGFloat) { } + @objc open override func reset() { + super.reset() + } - @objc public func setFont(_ font: UIFont, scale: Bool) { + @objc open func updateView(_ size: CGFloat) { } + + @objc open func setFont(_ font: UIFont, scale: Bool) { self.font = font setScale(scale) } - @objc public func setScale(_ scale: Bool) { + @objc open func setScale(_ scale: Bool) { if scale { standardFontSize = font.pointSize if let floatScale = scaleSize?.floatValue { @@ -354,6 +358,52 @@ public typealias ActionBlock = () -> () // Mark: - Old Helpers extension Label { + /** + Appends an external link image to the end of the attributed string. + Will provide one whitespace to the left of the icon; adds 2 chars to the end of the string. + */ + @objc public func appendExternalLinkIcon() { + + guard let attributedText = attributedText else { return } + + let mutableString = NSMutableAttributedString(attributedString: attributedText) + + mutableString.append(NSAttributedString(string: " ")) + mutableString.append(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize))) + self.attributedText = mutableString + } + + /** + Insert external link icon anywhere within text of Label. + + - Note: Each icon insertion adds 1 additional characters to the overall text length. + Therefore, you **MUST** insert icons and links in the order they would appear. + - parameter index: Location within the associated text to insert an external Link Icon + */ + public func insertExternalLinkIcon(at index: Int) { + + guard let attributedText = attributedText, index <= attributedText.string.count && index >= 0 else { return } + + let mutableString = NSMutableAttributedString(attributedString: attributedText) + mutableString.insert(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)), at: index) + + self.attributedText = mutableString + } + + /* + Retrieves an NSTextAttachment for NSAttributedString that is prepped to be inserted with the text. + + - parameter name: The Asset name of the image. DEFAULT: "externalLink" + - parameter dimension: length of the height and width of the image. Will be 80% the passed magnitude. + */ + static func getTextAttachmentImage(name: String = "externalLink", dimension: CGFloat) -> NSTextAttachment { + + let imageAttachment = NSTextAttachment() + imageAttachment.image = MVMCoreCache.shared()?.getImageFromRegisteredBundles(name) + imageAttachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) + + return imageAttachment + } public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect { @@ -424,6 +474,10 @@ extension Label { attributes?.append(textLink) } + @objc public func clearActionableClauses() { + attributes = attributes?.filter { !($0 is (any ActionLabelAttributeModel)) } + } + public func createActionBlockFor(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock? { return { [weak self] in guard let self = self else { return } From f05ea2aac652503fafc7f13ad1afcfdedcd2916f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Feb 2024 16:10:51 -0600 Subject: [PATCH 12/17] depricated methods and updated attribute addition Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift index 6d33d8ce..37eff177 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Label/Label.swift @@ -27,7 +27,9 @@ public typealias ActionBlock = () -> () public var standardFontSize: CGFloat = 0.0 /// Set this to use a custom sizing object during updateView instead of the standard. + @available(*, deprecated, message: "VDS is maintaining scaleSize") public var sizeObject: MFSizeObject? + @available(*, deprecated, message: "VDS is maintaining scaleSize") public var scaleSize: NSNumber? /// A specific text index to use as a unique marker. @@ -292,11 +294,6 @@ public typealias ActionBlock = () -> () @objc open func setScale(_ scale: Bool) { if scale { standardFontSize = font.pointSize - if let floatScale = scaleSize?.floatValue { - updateView(CGFloat(floatScale)) - } else { - updateView(MVMCoreUIUtility.getWidth()) - } } else { standardFontSize = 0 } @@ -471,7 +468,12 @@ extension Label { .sink { _ in actionBlock() } - attributes?.append(textLink) + if var attributes { + attributes.append(textLink) + setNeedsUpdate() + } else { + attributes = [textLink] + } } @objc public func clearActionableClauses() { From 908c3bddc39e5f360bd2c832e6f0e9488eae951f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Feb 2024 17:43:26 -0600 Subject: [PATCH 13/17] updated model Signed-off-by: Matt Bruce --- .../Atomic/Extensions/VDS-TextStyle.swift | 17 ++--- .../LockUps/TitleLockupModel.swift | 63 ++++++++++++------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift index 09fc5dcd..ba638e34 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift @@ -18,14 +18,15 @@ extension Styler.Font { return style } - public func vdsSubsetStyle() -> T? { - guard let style = vdsTextStyle() else { return nil } - guard let rawValue = style.rawValue as? T.RawValue, - let found = T(rawValue: rawValue) else { - print("Style: \(style.rawValue) is not in enum \(T.self)\ronly these cases exist:\r\(T.allCases)") - return nil + public func vdsSubsetStyle() throws -> T { + guard let style = vdsTextStyle(), let rawValue = style.toStandardStyle().rawValue as? T.RawValue, let standardStyle = T(rawValue: rawValue) else { + let err = "\(rawValue) was not found in the \(T.self), only these cases exist:\r\(T.allCases)" + throw MVMCoreError.errorObject(MVMCoreErrorObject(title: "\(T.self) conversion Issue", + messageToLog: err, + code: 999, + domain: ErrorDomainNative, + location: #file)!) } - return found + return standardStyle } } - diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift index bac8ef34..e11aea0f 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift @@ -90,38 +90,59 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco public func eyebrowModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.EyebrowModel? { guard let eyebrow else { return nil } let attrs = eyebrow.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) - let style: TextStyle? = eyebrow.fontStyle?.vdsTextStyle() - if let style, let standardStyle = VDS.TitleLockup.OtherStandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: eyebrow.text, isBold: style.isBold, standardStyle: standardStyle, textAttributes: attrs, numberOfLines: eyebrow.numberOfLines ?? 0) - } else { - return .init(text: eyebrow.text, textAttributes: attrs, numberOfLines: eyebrow.numberOfLines ?? 0) - } + do { + if let style = eyebrow.fontStyle { + return .init(text: eyebrow.text, + isBold: style.isBold(), + standardStyle: try style.vdsSubsetStyle(), + textAttributes: attrs, + numberOfLines: eyebrow.numberOfLines ?? 0) + } + } catch MVMCoreError.errorObject(let object) { + MVMCoreLoggingHandler.shared()?.addError(toLog: object) + } catch { } + + return .init(text: eyebrow.text, textAttributes: attrs, numberOfLines: eyebrow.numberOfLines ?? 0) } - public func titleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.TitleModel? { + public func titleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.TitleModel { let attrs = title.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) - let style: TextStyle? = title.fontStyle?.vdsTextStyle() - if let style, let standardStyle = VDS.TitleLockup.TitleStandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: title.text, textAttributes: attrs, isBold: style.isBold, standardStyle: standardStyle, numberOfLines: title.numberOfLines ?? 0) - } else { - return .init(text: title.text, textAttributes: attrs, numberOfLines: title.numberOfLines ?? 0) - } + do { + if let style = title.fontStyle { + return .init(text: title.text, + textAttributes: attrs, + isBold: style.isBold(), + standardStyle: try style.vdsSubsetStyle(), + numberOfLines: title.numberOfLines ?? 0) + } + + } catch MVMCoreError.errorObject(let object) { + MVMCoreLoggingHandler.shared()?.addError(toLog: object) + } catch { } + + return .init(text: title.text, textAttributes: attrs, numberOfLines: title.numberOfLines ?? 0) } public func subTitleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> VDS.TitleLockup.SubTitleModel? { guard let subTitle else { return nil } let attrs = subTitle.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) - let style: TextStyle? = subTitle.fontStyle?.vdsTextStyle() - if let style, let standardStyle = VDS.TitleLockup.OtherStandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: subTitle.text, standardStyle: standardStyle, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) - } else { - return .init(text: subTitle.text, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) - } + + do { + if let style = subTitle.fontStyle { + return .init(text: subTitle.text, + standardStyle: try style.vdsSubsetStyle(), + textAttributes: attrs, + numberOfLines: subTitle.numberOfLines ?? 0) + } + } catch MVMCoreError.errorObject(let object) { + MVMCoreLoggingHandler.shared()?.addError(toLog: object) + } catch { } + + return .init(text: subTitle.text, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) + } } - extension TitleLockupModel { public var surface: Surface { inverted ? .dark : .light } } - From 8daf1a2b728b47b0b3797feeee0e4933eaa4c51f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Feb 2024 17:56:50 -0600 Subject: [PATCH 14/17] updated more model properties Signed-off-by: Matt Bruce --- .../DesignedComponents/LockUps/TitleLockup.swift | 1 + .../LockUps/TitleLockupModel.swift | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift index b0fd7fea..6339b8e2 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockup.swift @@ -21,6 +21,7 @@ import VDS //-------------------------------------------------- open func viewModelDidUpdate() { surface = viewModel.surface + textAlignment = viewModel.textAlignment eyebrowModel = viewModel.eyebrowModel(delegateObject: delegateObject, additionalData: additionalData) titleModel = viewModel.titleModel(delegateObject: delegateObject, additionalData: additionalData) subTitleModel = viewModel.subTitleModel(delegateObject: delegateObject, additionalData: additionalData) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift index e11aea0f..034420cd 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift @@ -19,9 +19,11 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco public var moleculeName: String = TitleLockupModel.identifier public var id: String = UUID().uuidString + public var textAlignment: TitleLockup.TextAlignment = .left public var eyebrow: LabelModel? public var title: LabelModel public var subTitle: LabelModel? + public var subTitleColor: Use = .primary public var alignment: VDS.TitleLockup.TextAlignment = .left public var inverted: Bool = false @@ -49,9 +51,11 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco private enum CodingKeys: String, CodingKey { case id case moleculeName + case textAlignment case eyebrow case title case subTitle + case subTitleColor case inverted case alignment } @@ -63,10 +67,12 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString + textAlignment = try typeContainer.decodeIfPresent(TitleLockup.TextAlignment.self, forKey: .textAlignment) ?? .left title = try typeContainer.decode(LabelModel.self, forKey: .title) eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) subTitle = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .subTitle) - + subTitleColor = try typeContainer.decodeIfPresent(Use.self, forKey: .subTitleColor) ?? .primary + if let newAlignment = try typeContainer.decodeIfPresent(VDS.TitleLockup.TextAlignment.self, forKey: .alignment) { alignment = newAlignment } @@ -80,9 +86,11 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(textAlignment, forKey: .textAlignment) try container.encodeIfPresent(eyebrow, forKey: .eyebrow) try container.encodeModel(title, forKey: .title) try container.encodeIfPresent(subTitle, forKey: .subTitle) + try container.encode(subTitleColor, forKey: .subTitleColor) try container.encode(alignment, forKey: .alignment) try container.encode(inverted, forKey: .inverted) } @@ -131,6 +139,7 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco if let style = subTitle.fontStyle { return .init(text: subTitle.text, standardStyle: try style.vdsSubsetStyle(), + textColor: subTitleColor, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) } @@ -138,7 +147,7 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco MVMCoreLoggingHandler.shared()?.addError(toLog: object) } catch { } - return .init(text: subTitle.text, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) + return .init(text: subTitle.text, textColor: subTitleColor, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) } } From 881fd5e433c5cb378b4b971b6b662686b0e3a02c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 16 Feb 2024 09:37:44 -0600 Subject: [PATCH 15/17] refactored naming conventions in object Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/TileletModel.swift | 38 ++++++++++++------- .../LockUps/TitleLockupModel.swift | 2 +- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift index b41d9eef..7cc9747e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift @@ -68,23 +68,35 @@ open class TileletModel: MoleculeModelProtocol { public func titleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Tilelet.TitleModel? { guard let title else { return nil } let attrs = title.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) - let style: TextStyle? = title.fontStyle?.vdsTextStyle() - if let style, let standardStyle = Tilelet.TitleModel.StandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: title.text, textAttributes: attrs, standardStyle: standardStyle) - } else { - return .init(text: title.text, textAttributes: attrs) - } - } + + do { + if let style = title.fontStyle { + return .init(text: title.text, + textAttributes: attrs, + standardStyle: try style.vdsSubsetStyle()) + } + } catch MVMCoreError.errorObject(let object) { + MVMCoreLoggingHandler.shared()?.addError(toLog: object) + } catch { } + + return .init(text: title.text, textAttributes: attrs) + } + public func subTitleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Tilelet.SubTitleModel? { guard let subTitle else { return nil } let attrs = subTitle.attributes?.toVDSLabelAttributeModel(delegateObject: delegateObject, additionalData: additionalData) - let style: TextStyle? = subTitle.fontStyle?.vdsTextStyle() - if let style, let standardStyle = Tilelet.SubTitleModel.StandardStyle(rawValue: style.toStandardStyle().rawValue) { - return .init(text: subTitle.text, textAttributes: attrs, standardStyle: standardStyle) - } else { - return .init(text: subTitle.text, textAttributes: attrs) - } + do { + if let style = subTitle.fontStyle { + return .init(text: subTitle.text, + otherStandardStyle: try style.vdsSubsetStyle(), + textAttributes: attrs) + } + } catch MVMCoreError.errorObject(let object) { + MVMCoreLoggingHandler.shared()?.addError(toLog: object) + } catch { } + + return .init(text: subTitle.text, textAttributes: attrs) } public func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift index 034420cd..6c48398b 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift @@ -138,7 +138,7 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco do { if let style = subTitle.fontStyle { return .init(text: subTitle.text, - standardStyle: try style.vdsSubsetStyle(), + otherStandardStyle: try style.vdsSubsetStyle(), textColor: subTitleColor, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) From 995bec6cb2a31cc9f5193f13a9656294a8e6f01e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 28 Feb 2024 15:32:00 -0600 Subject: [PATCH 16/17] refactored out mapped and inserted within the only function that calls it. Signed-off-by: Matt Bruce --- .../Atomic/Extensions/VDS-TextStyle.swift | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift index 6da79f1f..f7e5917e 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-TextStyle.swift @@ -12,7 +12,25 @@ import VDS extension Styler.Font { //Converts Legacy Font to a VDS.TextStyle public func vdsTextStyle() -> VDS.TextStyle? { - let updatedRaw = mapped().rawValue.replacingOccurrences(of: "Regular", with: "") + + //ensure that the current Styler.Font isn't Legacy Version + //if it is, here is the conversion to the updated version + var actualFont: Styler.Font = self + switch self { + case .Title2XLarge: actualFont = .RegularTitle2XLarge + case .TitleXLarge: actualFont = .RegularTitleXLarge + case .H1: actualFont = .RegularTitle2XLarge + case .H32: actualFont = .RegularTitleXLarge + case .H2: actualFont = .RegularTitleLarge + case .B20: actualFont = .RegularBodyLarge + case .H3: actualFont = .BoldTitleMedium + case .B1: actualFont = .BoldBodySmall + case .B2: actualFont = .RegularBodySmall + case .B3: actualFont = .RegularMicro + default: break + } + + let updatedRaw = actualFont.rawValue.replacingOccurrences(of: "Regular", with: "") let newRaw = updatedRaw.prefix(1).lowercased() + updatedRaw.dropFirst() guard let style = VDS.TextStyle(rawValue: newRaw) else { return nil } return style @@ -29,24 +47,6 @@ extension Styler.Font { } return standardStyle } - - private func mapped() -> Styler.Font { - var mapped: Styler.Font = self - switch self { - case .Title2XLarge: mapped = .RegularTitle2XLarge - case .TitleXLarge: mapped = .RegularTitleXLarge - case .H1: mapped = .RegularTitle2XLarge - case .H32: mapped = .RegularTitleXLarge - case .H2: mapped = .RegularTitleLarge - case .B20: mapped = .RegularBodyLarge - case .H3: mapped = .BoldTitleMedium - case .B1: mapped = .BoldBodySmall - case .B2: mapped = .RegularBodySmall - case .B3: mapped = .RegularMicro - default: break - } - return mapped - } } extension VDS.Font { From fb2a866f14ca7efd5c4b4e2c0b1aa1fe6910b7a2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 28 Feb 2024 16:07:57 -0600 Subject: [PATCH 17/17] added depreciated coding keys also reverted to non-hard typed LabelModel as done previous. Signed-off-by: Matt Bruce --- .../LockUps/TitleLockupModel.swift | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift index ce70a338..3c9f25fd 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/LockUps/TitleLockupModel.swift @@ -74,20 +74,33 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco let typeContainer = try decoder.container(keyedBy: CodingKeys.self) id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString textAlignment = try typeContainer.decodeIfPresent(TitleLockup.TextAlignment.self, forKey: .textAlignment) ?? .left - title = try typeContainer.decode(LabelModel.self, forKey: .title) + title = try typeContainer.decodeMolecule(codingKey: .title) eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) - subTitle = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .subTitle) - subTitleColor = try typeContainer.decodeIfPresent(Use.self, forKey: .subTitleColor) ?? .primary + subTitle = try typeContainer.decodeMoleculeIfPresent(codingKey: .subTitle) + + /// look for color hex code + if let color = try? typeContainer.decodeIfPresent(Color.self, forKey: .subTitleColor) { + self.subTitleColor = color.uiColor.isDark() ? .primary : .secondary + + } else if let subTitleColor = try? typeContainer.decodeIfPresent(Use.self, forKey: .subTitleColor) { + self.subTitleColor = subTitleColor + + } else { + subTitleColor = .primary + } if let newAlignment = try typeContainer.decodeIfPresent(VDS.TitleLockup.TextAlignment.self, forKey: .alignment) { alignment = newAlignment } - if let invertedStatus = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { - inverted = invertedStatus + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } else { + try setInverted(deprecatedFrom: decoder) } + } - + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) @@ -156,6 +169,24 @@ public class TitleLockupModel: MoleculeModelProtocol, ParentMoleculeModelProtoco return .init(text: subTitle.text, textColor: subTitleColor, textAttributes: attrs, numberOfLines: subTitle.numberOfLines ?? 0) } + + private enum DeprecatedCodingKeys: String, CodingKey { + case titleColor + case backgroundColor + } + + private func setInverted(deprecatedFrom decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: DeprecatedCodingKeys.self) + + if let titleColor = try typeContainer.decodeIfPresent(Color.self, forKey: .titleColor) { + inverted = !titleColor.uiColor.isDark() + } + + if let backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) { + inverted = !backgroundColor.uiColor.isDark() + } + } + } extension TitleLockupModel {