From 82a8a32ccbd1f79c6144b83d7a6bc2a328917dc9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 29 Feb 2024 10:14:42 -0600 Subject: [PATCH 01/10] refactored into base class for dealing with TileContainer Padding --- .../TileContainer/TileContainer.swift | 75 +++++++++++-------- VDS/Components/Tilelet/Tilelet.swift | 52 ++++++++----- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 98090ae4..0484e815 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -10,8 +10,45 @@ import VDSColorTokens import VDSFormControlsTokens import UIKit +public protocol Valuing { + static var defaultValue: Self { get } + var value: CGFloat { get } +} + @objc(VDSTileContainer) -open class TileContainer: Control { +open class TileContainer: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: Valuing { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom(CGFloat) + + public static var defaultValue: Self { .padding4X } + + public var value: CGFloat { + switch self { + case .padding2X: + return VDSLayout.Spacing.space2X.value + case .padding4X: + return VDSLayout.Spacing.space4X.value + case .padding6X: + return VDSLayout.Spacing.space6X.value + case .padding8X: + return VDSLayout.Spacing.space8X.value + case .padding12X: + return VDSLayout.Spacing.space12X.value + case .custom(let padding): + return padding + } + } + } +} + +open class TileContainerBase: Control { //-------------------------------------------------- // MARK: - Initializers @@ -53,33 +90,6 @@ open class TileContainer: Control { case gradient(String, String) case none } - - /// Enum used to describe the padding choices used for this component. - public enum Padding { - case padding2X - case padding4X - case padding6X - case padding8X - case padding12X - case custom(CGFloat) - - public var value: CGFloat { - switch self { - case .padding2X: - return VDSLayout.Spacing.space2X.value - case .padding4X: - return VDSLayout.Spacing.space4X.value - case .padding6X: - return VDSLayout.Spacing.space6X.value - case .padding8X: - return VDSLayout.Spacing.space8X.value - case .padding12X: - return VDSLayout.Spacing.space12X.value - case .custom(let padding): - return padding - } - } - } /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { @@ -130,7 +140,7 @@ open class TileContainer: Control { open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } /// Sets the inside padding for the component - open var padding: Padding = .padding4X { didSet { setNeedsUpdate() } } + open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } @@ -253,7 +263,6 @@ open class TileContainer: Control { super.reset() shouldUpdateView = false color = .white - padding = .padding4X aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil @@ -395,7 +404,7 @@ open class TileContainer: Control { } -extension TileContainer { +extension TileContainerBase { struct DropshadowConfiguration: Dropshadowable { var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with { @@ -408,7 +417,7 @@ extension TileContainer { final class BackgroundColorConfiguration: ObjectColorable { - typealias ObjectType = TileContainer + typealias ObjectType = TileContainerBase let primaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) let secondaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) @@ -418,7 +427,7 @@ extension TileContainer { required init() { } - func getColor(_ object: TileContainer) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { switch object.color { case .primary: primaryColorConfig.getColor(object.surface) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 657a735b..3d5fe495 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -16,7 +16,38 @@ import Combine /// while it can include an arrow CTA, it does not require one in order to /// function. @objc(VDSTilelet) -open class Tilelet: TileContainer { +open class Tilelet: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: String, Valuing, CaseIterable { + case small + case large + + public static var defaultValue: Self { .large } + + public var value: CGFloat { + switch self { + case .small: + return UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value + case .large: + return UIDevice.isIPad ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value + } + } + + fileprivate var titleLockupBottomSpacing: CGFloat { + switch self.value { + case VDSLayout.Spacing.space3X.value: + return VDSLayout.Spacing.space4X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space6X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space8X.value + default: + return VDSLayout.Spacing.space4X.value + } + } + } + //-------------------------------------------------- // MARK: - Initializers @@ -392,7 +423,7 @@ open class Tilelet: TileContainer { view = titleLockupContainerView } if let view { - stackView.setCustomSpacing(padding.tiletSpacing, after: view) + stackView.setCustomSpacing(padding.titleLockupBottomSpacing, after: view) } if iconContainerView.superview == nil { stackView.addArrangedSubview(iconContainerView) @@ -403,20 +434,3 @@ open class Tilelet: TileContainer { } } } - -extension TileContainer.Padding { - fileprivate var tiletSpacing: CGFloat { - switch self { - case .padding2X: - return 16 - case .padding4X: - return 24 - case .padding6X: - return 32 - case .padding8X: - return 48 - default: - return 16 - } - } -} From add8e974c02762950aba14ce2a48645f83160c70 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Fri, 1 Mar 2024 17:17:04 +0530 Subject: [PATCH 02/10] added tilelet linebreak mode config & text position --- VDS.xcodeproj/project.pbxproj | 4 + VDS/Components/Badge/Badge.swift | 1 + VDS/Components/Tilelet/Tilelet.swift | 121 ++++++++++++++++-- .../Tilelet/TileletBadgeModel.swift | 7 +- .../Tilelet/TileletSubTitleModel.swift | 13 +- .../Tilelet/TileletTitleModel.swift | 19 ++- VDS/Components/Tilelet/TitleletChangeLog.txt | 110 ++++++++++++++++ VDS/Components/TitleLockup/TitleLockup.swift | 4 +- .../TitleLockupSubTitleModel.swift | 8 +- .../TitleLockup/TitleLockupTitleModel.swift | 8 +- 10 files changed, 275 insertions(+), 20 deletions(-) create mode 100644 VDS/Components/Tilelet/TitleletChangeLog.txt diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index ef6acb80..5d78557f 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; }; 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; @@ -180,6 +181,7 @@ 44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; + 710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dropshadowable.swift; sourceTree = ""; }; 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; @@ -676,6 +678,7 @@ EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */, EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */, + 710607942B91A99500F2863F /* TitleletChangeLog.txt */, ); path = Tilelet; sourceTree = ""; @@ -947,6 +950,7 @@ 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */, EAEEECA72B1F952000531FC2 /* TabsChangeLog.txt in Resources */, EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */, + 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */, EA5F86CC2A1D28B500BC83E4 /* ReleaseNotes.txt in Resources */, EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */, EAEEECA22B1F92AD00531FC2 /* LabelChangeLog.txt in Resources */, diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index f6bb36d5..bbe1233d 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -147,6 +147,7 @@ open class Badge: View { label.widthGreaterThanEqualTo(constant: minWidth) maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false } + clipsToBounds = true } /// Resets to default settings. diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 657a735b..e562ff0a 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -39,6 +39,7 @@ open class Tilelet: TileContainer { /// Enum to represent the Vertical Layout of the Text. public enum TextPosition: String, CaseIterable { case top + case middle case bottom } @@ -72,7 +73,7 @@ open class Tilelet: TileContainer { //-------------------------------------------------- // MARK: - Public Properties - //-------------------------------------------------- + //-------------------------------------------------- public override var onClickSubscriber: AnyCancellable? { didSet { isAccessibilityElement = onClickSubscriber != nil @@ -82,6 +83,27 @@ open class Tilelet: TileContainer { /// Title lockup positioned in the contentView. open var titleLockup = TitleLockup().with { $0.standardStyleConfiguration = .init(styleConfigurations: [ + .init(deviceType: .iPhone, + titleStandardStyles: [.bodySmall], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodySmall], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPhone, + titleStandardStyles: [.bodyMedium], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyMedium], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPhone, + titleStandardStyles: [.bodyLarge], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyLarge], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), .init(deviceType: .iPhone, titleStandardStyles: [.titleSmall], spacingConfigurations: [ @@ -103,6 +125,27 @@ open class Tilelet: TileContainer { topSpacing: VDSLayout.Spacing.space3X.value, bottomSpacing: VDSLayout.Spacing.space3X.value) ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodySmall], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodySmall], + topSpacing: VDSLayout.Spacing.space2X.value, + bottomSpacing: VDSLayout.Spacing.space2X.value) + ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodyMedium], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyMedium], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), + .init(deviceType: .iPad, + titleStandardStyles: [.bodyLarge], + spacingConfigurations: [ + .init(otherStandardStyles: [.bodyLarge], + topSpacing: VDSLayout.Spacing.space1X.value, + bottomSpacing: VDSLayout.Spacing.space1X.value) + ]), .init(deviceType: .iPad, titleStandardStyles: [.titleSmall, .titleMedium], spacingConfigurations: [ @@ -206,6 +249,12 @@ open class Tilelet: TileContainer { //-------------------------------------------------- internal var titleLockupWidthConstraint: NSLayoutConstraint? internal var titleLockupTrailingConstraint: NSLayoutConstraint? + internal var titleLockupTopConstraint: NSLayoutConstraint? + internal var titleLockupBottomConstraint: NSLayoutConstraint? + internal var titleLockupTopGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupBottomGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupCenterYConstraint: NSLayoutConstraint? + internal var titleLockupTitleLabelBottomConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -230,11 +279,16 @@ open class Tilelet: TileContainer { titleLockupContainerView.addSubview(titleLockup) titleLockup - .pinTop() .pinLeading() - .pinBottom() + titleLockupTopConstraint = titleLockup.topAnchor.constraint(equalTo: titleLockupContainerView.topAnchor) + titleLockupTopConstraint?.activate() + titleLockupBottomConstraint = titleLockupContainerView.bottomAnchor.constraint(equalTo: titleLockup.bottomAnchor) + titleLockupBottomConstraint?.activate() titleLockupTrailingConstraint = titleLockup.trailingAnchor.constraint(equalTo: titleLockupContainerView.trailingAnchor) titleLockupTrailingConstraint?.isActive = true + titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) + titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) + titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(directionalIcon) @@ -249,6 +303,12 @@ open class Tilelet: TileContainer { .pinTop() .pinBottom() + padding = .custom( UIDevice.isIPad ? VDSLayout.Spacing.space6X.value : VDSLayout.Spacing.space4X.value) + //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + var labelPriority = UILayoutPriority.defaultHigh.rawValue + titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) + badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) + titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) } /// Resets to default settings. @@ -261,7 +321,7 @@ open class Tilelet: TileContainer { titleModel = nil subTitleModel = nil descriptiveIconModel = nil - directionalIconModel = nil + directionalIconModel = nil shouldUpdateView = true setNeedsUpdate() } @@ -273,7 +333,11 @@ open class Tilelet: TileContainer { updateBadge() updateTitleLockup() updateIcons() - + ///Content-driven height Tilelets - Minimum height is configurable. + ///if width != nil && (aspectRatio != .none || height != nil) then tilelet is not self growing, so we can apply text position alignments. + if width != nil && (aspectRatio != .none || height != nil) { + updateTextPositionAlignment() + } layoutIfNeeded() } @@ -401,20 +465,59 @@ open class Tilelet: TileContainer { } else { removeFromSuperview(iconContainerView) } + if let lastElement = titleLockup.lastElement { + titleLockupTitleLabelBottomConstraint?.deactivate() + let anchorConstraint = showIconContainerView ? iconContainerView.topAnchor : containerView.bottomAnchor + titleLockupTitleLabelBottomConstraint = anchorConstraint.constraint(greaterThanOrEqualTo: lastElement.bottomAnchor) + titleLockupTitleLabelBottomConstraint?.activate() + } + } + + private func updateTextPositionAlignment() { + switch textPostion { + case .top: + titleLockupTopConstraint?.activate() + titleLockupTopGreaterThanConstraint?.deactivate() + titleLockupBottomConstraint?.deactivate() + titleLockupBottomGreaterThanConstraint?.activate() + titleLockupCenterYConstraint?.deactivate() + case .middle: + titleLockupTopConstraint?.deactivate() + titleLockupTopGreaterThanConstraint?.activate() + titleLockupBottomConstraint?.deactivate() + titleLockupBottomGreaterThanConstraint?.activate() + titleLockupCenterYConstraint?.activate() + case .bottom: + titleLockupTopConstraint?.deactivate() + titleLockupTopGreaterThanConstraint?.activate() + titleLockupBottomConstraint?.activate() + titleLockupBottomGreaterThanConstraint?.deactivate() + titleLockupCenterYConstraint?.deactivate() + } + } +} + +extension TitleLockup { + + fileprivate var lastElement: UIView? { + guard subTitleModel == nil else { return subTitleLabel } + guard titleModel == nil else { return titleLabel } + return nil } } extension TileContainer.Padding { + fileprivate var tiletSpacing: CGFloat { switch self { - case .padding2X: - return 16 case .padding4X: return 24 case .padding6X: return 32 - case .padding8X: - return 48 + case .custom(let padding): + if padding == VDSLayout.Spacing.space3X.value { + return 16 + } else { fallthrough } default: return 16 } diff --git a/VDS/Components/Tilelet/TileletBadgeModel.swift b/VDS/Components/Tilelet/TileletBadgeModel.swift index f58c134c..9dbd03fe 100644 --- a/VDS/Components/Tilelet/TileletBadgeModel.swift +++ b/VDS/Components/Tilelet/TileletBadgeModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { @@ -26,12 +27,16 @@ extension Tilelet { /// Max width that will be used for the badge. public var maxWidth: CGFloat? - public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil) { + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + + public init(text: String, fillColor: Badge.FillColor = .red, surface: Surface = .light, numberOfLines: Int = 0, maxWidth: CGFloat? = nil, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.fillColor = fillColor self.surface = surface self.numberOfLines = numberOfLines self.maxWidth = maxWidth + self.lineBreakMode = lineBreakMode } } } diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index dab4c81c..63820316 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { /// Model that represents the options available for the sub title label. @@ -18,7 +19,8 @@ extension Tilelet { case bodyLarge case bodyMedium case bodySmall - + case titleSmall + case titleMedium public var defaultValue: TitleLockup.OtherStandardStyle { .bodySmall } } //-------------------------------------------------- @@ -36,17 +38,22 @@ extension Tilelet { /// Text color that will be used for the subTitle label. public var textColor: Use = .primary + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public init(text: String, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .bodySmall) { + standardStyle: StandardStyle = .bodySmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.textAttributes = textAttributes self.textColor = textColor self.standardStyle = standardStyle + self.lineBreakMode = lineBreakMode } //-------------------------------------------------- @@ -57,7 +64,7 @@ extension Tilelet { TitleLockup.SubTitleModel(text: text, standardStyle: standardStyle.value, textColor: textColor, - textAttributes: textAttributes) + textAttributes: textAttributes, lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TileletTitleModel.swift b/VDS/Components/Tilelet/TileletTitleModel.swift index 728de07b..fa8c603d 100644 --- a/VDS/Components/Tilelet/TileletTitleModel.swift +++ b/VDS/Components/Tilelet/TileletTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension Tilelet { /// Model that represents the options available for the title label. @@ -19,7 +20,10 @@ extension Tilelet { case titleLarge case titleMedium case titleSmall - + case bodyLarge + case bodyMedium + case bodySmall + public var defaultValue: TitleLockup.TitleStandardStyle { .titleSmall } } //-------------------------------------------------- @@ -28,21 +32,30 @@ extension Tilelet { /// Text that will be used for the title label. public var text: String = "" + /// Used in combination with standardStyle to set the textStyle that will be used for the title label. + public var isBold: Bool = false /// Text attributes that will be used for the title label. public var textAttributes: [any LabelAttributeModel]? /// Text style that will be used for the title label. public var standardStyle: StandardStyle = .titleSmall + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public init(text: String, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .titleSmall) { + isBold: Bool = true, + standardStyle: StandardStyle = .titleSmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.textAttributes = textAttributes self.standardStyle = standardStyle + self.isBold = isBold + self.lineBreakMode = lineBreakMode } //-------------------------------------------------- @@ -52,7 +65,7 @@ extension Tilelet { public func toTitleLockupTitleModel() -> TitleLockup.TitleModel { TitleLockup.TitleModel(text: text, textAttributes: textAttributes, - standardStyle: standardStyle.value) + isBold: isBold, standardStyle: standardStyle.value, lineBreakMode: lineBreakMode) } } } diff --git a/VDS/Components/Tilelet/TitleletChangeLog.txt b/VDS/Components/Tilelet/TitleletChangeLog.txt new file mode 100644 index 00000000..bbbc8edf --- /dev/null +++ b/VDS/Components/Tilelet/TitleletChangeLog.txt @@ -0,0 +1,110 @@ +MM/DD/YYYY +---------------- + +08/31/2022 +---------------- +- Updates to Typography and default Title Lockup configurations: +- Default +- Regular title updated to bold weight +- Subtitle updated to primary color +- Inverted +- Regular title updated to bold weight +- Subtitle updated to primary color + +07/15/2022 +---------------- +- Updates to Typography and default Title Lockup configurations: +- Default +- Bold Title updated to Regular weight +- Subtitle updated to secondary color +- Inverted +- Bold Title updated to Regular weight +- Subtitle updated to secondary color + +04/27/2022 +---------------- +- Updates to Title Lockup configurations. + +04/14/2022 +---------------- +- Updates to Tilelet Group spec based on 4/14 handoff feedback. + +04/04/2022 +---------------- +- Visuals added to Tilelet Group spec. + +04/01/2022 +---------------- +- Updates based on 3/31 handoff feedback. + +03/30/2022 +---------------- +- Tilelet added to Test App. + +03/29/2022 +---------------- +- Interactive states updated. + +03/22/2022 +---------------- +- Layout and spacing page added. + +03/21/2022 +---------------- +- Updated anatomy, configuration, and element pages. + +02/21/2022 +---------------- +- Title Lockup’s width properties added. + +09/14/2022 +---------------- +- States section updated to reflect states inherited from Tile Container +- Dev note added to Layouts. + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced focus border pixel and style & spacing values with tokens. + +06/19/2023 +---------------- +- Updated anatomy element names to be local to Tilelet and indicated the core component that powers them under the hood. + +09/21/2023 +---------------- +- Updated Anatomy to include Title Lockup with Eyebrow and Tooltip +- Updated Configurations to include Title Lockup section +- Updated Layout and spacing to include Alignment (Left, Center) and textPosition (Middle)
 +- Updated Elements to include Eyebrow and uniform 1:1 pairings + +10/09/2023 +---------------- +- Updated Configurations VDS Title Lockup and VDS Tile Container sections. + +10/13/2023 +---------------- +- SPEC format changes to Anatomy to include nested component configurations +- Removed Configurations page + +11/20/2023 +---------------- +- Updated visuals to reflect new corner radius value - 12px +- Updated focus border corner radius to 14px +- View changes + +12/14/2023 +---------------- +- Added secondary, primary backgroundColor options +- Relabeled configurations section headings to use property names +- Relabeled surface sections to use “surface:” labeling convention +- Deprecated gray backgroundColor +- View changes + +02/08/2024 +---------------- +- Added external-icon option to Descriptive Icon in Configurations +- View changes diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index dbfd8637..6dc42e70 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -350,7 +350,7 @@ open class TitleLockup: View { titleLabel.attributes = titleModel.textAttributes titleLabel.numberOfLines = titleModel.numberOfLines titleLabel.surface = surface - + titleLabel.lineBreakMode = titleModel.lineBreakMode addSubview(titleLabel) titleLabel .pinTop(previousView?.bottomAnchor ?? self.topAnchor, eyebrowLabel == previousView ? topSpacing : 0) @@ -369,7 +369,7 @@ open class TitleLockup: View { subTitleLabel.attributes = subTitleModel.textAttributes subTitleLabel.numberOfLines = subTitleModel.numberOfLines subTitleLabel.surface = surface - + subTitleLabel.lineBreakMode = subTitleModel.lineBreakMode addSubview(subTitleLabel) subTitleLabel .pinTop(previousView?.bottomAnchor ?? self.topAnchor, (eyebrowLabel == previousView || titleLabel == previousView) ? bottomSpacing : 0) diff --git a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift index 3ba6c7ff..5ad105ef 100644 --- a/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupSubTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension TitleLockup { /// Model that represents the options available for the sub title label. @@ -25,16 +26,21 @@ extension TitleLockup { /// Number of lines used in the subtitle label. public var numberOfLines: Int + /// LineBreakMode used in subtitle label. + public var lineBreakMode: NSLineBreakMode + public init(text: String, standardStyle: OtherStandardStyle = .bodyLarge, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - numberOfLines: Int = 0) { + numberOfLines: Int = 0, + lineBreakMode: NSLineBreakMode = .byWordWrapping) { self.text = text self.standardStyle = standardStyle self.textColor = textColor self.textAttributes = textAttributes self.numberOfLines = numberOfLines + self.lineBreakMode = lineBreakMode } /// TextStyle used to render the text. diff --git a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift index 2b957276..7541151f 100644 --- a/VDS/Components/TitleLockup/TitleLockupTitleModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupTitleModel.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit extension TitleLockup { /// Model that represents the options available for the sub title label. @@ -25,16 +26,21 @@ extension TitleLockup { /// Number of lines that will be used for the title label. public var numberOfLines: Int + /// LineBreakMode used in subtitle label. + public var lineBreakMode: NSLineBreakMode + public init(text: String, textAttributes: [any LabelAttributeModel]? = nil, isBold: Bool = true, standardStyle: TitleStandardStyle = .featureXSmall, - numberOfLines: Int = 0) { + numberOfLines: Int = 0, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { self.text = text self.isBold = isBold self.textAttributes = textAttributes self.standardStyle = standardStyle self.numberOfLines = numberOfLines + self.lineBreakMode = lineBreakMode } /// TextStyle used to render the text. From 963e31acd3e98759c0b815b6e5507ecb6bff0fe5 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:28:17 -0600 Subject: [PATCH 03/10] Adding the padding fix between TileContainer/Tilelet Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 8 +++ .../TileContainer/TileContainer.swift | 70 ++++++++++--------- VDS/Components/Tilelet/Tilelet.swift | 54 ++++++++------ VDS/Extensions/VDSLayout.swift | 4 +- VDS/Protocols/EnumSubset.swift | 2 +- VDS/Protocols/EnumValuing.swift | 12 ++++ VDS/Protocols/Valuing.swift | 13 ++++ 7 files changed, 107 insertions(+), 56 deletions(-) create mode 100644 VDS/Protocols/EnumValuing.swift create mode 100644 VDS/Protocols/Valuing.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 5d78557f..4b863360 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -99,6 +99,8 @@ EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; + EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* EnumValuing.swift */; }; + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8992B927108006A3869 /* Valuing.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */; }; @@ -268,6 +270,8 @@ EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; + EAACB8972B92706F006A3869 /* EnumValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumValuing.swift; sourceTree = ""; }; + EAACB8992B927108006A3869 /* Valuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valuing.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Typography+Base.swift"; sourceTree = ""; }; @@ -552,6 +556,7 @@ EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, + EAACB8972B92706F006A3869 /* EnumValuing.swift */, EAF7F0A1289AFB3900B287F5 /* Errorable.swift */, EA3361AE288B26310071C351 /* FormFieldable.swift */, EA33624628931B050071C351 /* Initable.swift */, @@ -562,6 +567,7 @@ EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */, + EAACB8992B927108006A3869 /* Valuing.swift */, 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */, ); path = Protocols; @@ -1001,6 +1007,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, + EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, @@ -1016,6 +1023,7 @@ EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, + EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 98090ae4..27cfa1f4 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -11,7 +11,39 @@ import VDSFormControlsTokens import UIKit @objc(VDSTileContainer) -open class TileContainer: Control { +open class TileContainer: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: EnumValuing { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom(CGFloat) + + public static var defaultValue: Self { .padding4X } + + public var value: CGFloat { + switch self { + case .padding2X: + return VDSLayout.Spacing.space2X.value + case .padding4X: + return VDSLayout.Spacing.space4X.value + case .padding6X: + return VDSLayout.Spacing.space6X.value + case .padding8X: + return VDSLayout.Spacing.space8X.value + case .padding12X: + return VDSLayout.Spacing.space12X.value + case .custom(let padding): + return padding + } + } + } +} + +open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers @@ -53,33 +85,6 @@ open class TileContainer: Control { case gradient(String, String) case none } - - /// Enum used to describe the padding choices used for this component. - public enum Padding { - case padding2X - case padding4X - case padding6X - case padding8X - case padding12X - case custom(CGFloat) - - public var value: CGFloat { - switch self { - case .padding2X: - return VDSLayout.Spacing.space2X.value - case .padding4X: - return VDSLayout.Spacing.space4X.value - case .padding6X: - return VDSLayout.Spacing.space6X.value - case .padding8X: - return VDSLayout.Spacing.space8X.value - case .padding12X: - return VDSLayout.Spacing.space12X.value - case .custom(let padding): - return padding - } - } - } /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { @@ -130,7 +135,7 @@ open class TileContainer: Control { open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } /// Sets the inside padding for the component - open var padding: Padding = .padding4X { didSet { setNeedsUpdate() } } + open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } @@ -253,7 +258,6 @@ open class TileContainer: Control { super.reset() shouldUpdateView = false color = .white - padding = .padding4X aspectRatio = .ratio1x1 imageFallbackColor = .light width = nil @@ -395,7 +399,7 @@ open class TileContainer: Control { } -extension TileContainer { +extension TileContainerBase { struct DropshadowConfiguration: Dropshadowable { var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with { @@ -408,7 +412,7 @@ extension TileContainer { final class BackgroundColorConfiguration: ObjectColorable { - typealias ObjectType = TileContainer + typealias ObjectType = TileContainerBase let primaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) let secondaryColorConfig = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark) @@ -418,7 +422,7 @@ extension TileContainer { required init() { } - func getColor(_ object: TileContainer) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { switch object.color { case .primary: primaryColorConfig.getColor(object.surface) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index e562ff0a..77d6c70e 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -16,7 +16,38 @@ import Combine /// while it can include an arrow CTA, it does not require one in order to /// function. @objc(VDSTilelet) -open class Tilelet: TileContainer { +open class Tilelet: TileContainerBase { + + /// Enum used to describe the padding choices used for this component. + public enum Padding: String, EnumValuing, CaseIterable { + case small + case large + + public static var defaultValue: Self { .large } + + public var value: CGFloat { + switch self { + case .small: + return UIDevice.isIPad ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value + case .large: + return UIDevice.isIPad ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value + } + } + + fileprivate var titleLockupBottomSpacing: CGFloat { + switch self.value { + case VDSLayout.Spacing.space3X.value: + return VDSLayout.Spacing.space4X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space6X.value + case VDSLayout.Spacing.space4X.value: + return VDSLayout.Spacing.space8X.value + default: + return VDSLayout.Spacing.space4X.value + } + } + } + //-------------------------------------------------- // MARK: - Initializers @@ -303,7 +334,6 @@ open class Tilelet: TileContainer { .pinTop() .pinBottom() - padding = .custom( UIDevice.isIPad ? VDSLayout.Spacing.space6X.value : VDSLayout.Spacing.space4X.value) //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). var labelPriority = UILayoutPriority.defaultHigh.rawValue titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) @@ -456,7 +486,7 @@ open class Tilelet: TileContainer { view = titleLockupContainerView } if let view { - stackView.setCustomSpacing(padding.tiletSpacing, after: view) + stackView.setCustomSpacing(padding.titleLockupBottomSpacing, after: view) } if iconContainerView.superview == nil { stackView.addArrangedSubview(iconContainerView) @@ -505,21 +535,3 @@ extension TitleLockup { return nil } } - -extension TileContainer.Padding { - - fileprivate var tiletSpacing: CGFloat { - switch self { - case .padding4X: - return 24 - case .padding6X: - return 32 - case .custom(let padding): - if padding == VDSLayout.Spacing.space3X.value { - return 16 - } else { fallthrough } - default: - return 16 - } - } -} diff --git a/VDS/Extensions/VDSLayout.swift b/VDS/Extensions/VDSLayout.swift index f30a0635..30df201b 100644 --- a/VDS/Extensions/VDSLayout.swift +++ b/VDS/Extensions/VDSLayout.swift @@ -10,7 +10,9 @@ import Foundation /// Represents constants used that deal with layout. public struct VDSLayout { /// Enum used to describe the spacing constants. - public enum Spacing: String, CaseIterable { + public enum Spacing: String, CaseIterable, EnumValuing { + public static var defaultValue: VDSLayout.Spacing = .space2X + case space1X case space2X case space3X diff --git a/VDS/Protocols/EnumSubset.swift b/VDS/Protocols/EnumSubset.swift index a690fca3..831ce1fb 100644 --- a/VDS/Protocols/EnumSubset.swift +++ b/VDS/Protocols/EnumSubset.swift @@ -7,7 +7,7 @@ import Foundation -public protocol EnumSubset: RawRepresentable, CaseIterable { +public protocol EnumSubset: RawRepresentable, CaseIterable, Valuing { associatedtype T:RawRepresentable var defaultValue: T { get } } diff --git a/VDS/Protocols/EnumValuing.swift b/VDS/Protocols/EnumValuing.swift new file mode 100644 index 00000000..b8f27824 --- /dev/null +++ b/VDS/Protocols/EnumValuing.swift @@ -0,0 +1,12 @@ +// +// EnumValuing.swift +// VDS +// +// Created by Matt Bruce on 3/1/24. +// + +import Foundation + +public protocol EnumValuing: Valuing { + static var defaultValue: Self { get } +} diff --git a/VDS/Protocols/Valuing.swift b/VDS/Protocols/Valuing.swift new file mode 100644 index 00000000..61cea9e8 --- /dev/null +++ b/VDS/Protocols/Valuing.swift @@ -0,0 +1,13 @@ +// +// Valuing.swift +// VDS +// +// Created by Matt Bruce on 3/1/24. +// + +import Foundation + +public protocol Valuing { + associatedtype ValueType + var value: ValueType { get } +} From 42ad973396001ba55c7cfe9111b5f5a8a6b02545 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:43:32 -0600 Subject: [PATCH 04/10] refactored enum Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 8 ++++---- VDS/Protocols/{EnumValuing.swift => DefaultValuing.swift} | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) rename VDS/Protocols/{EnumValuing.swift => DefaultValuing.swift} (52%) diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 4b863360..d6c5f3f5 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -99,7 +99,7 @@ EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; - EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* EnumValuing.swift */; }; + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8972B92706F006A3869 /* DefaultValuing.swift */; }; EAACB89A2B927108006A3869 /* Valuing.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAACB8992B927108006A3869 /* Valuing.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; @@ -270,7 +270,7 @@ EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; - EAACB8972B92706F006A3869 /* EnumValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumValuing.swift; sourceTree = ""; }; + EAACB8972B92706F006A3869 /* DefaultValuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultValuing.swift; sourceTree = ""; }; EAACB8992B927108006A3869 /* Valuing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valuing.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; @@ -553,10 +553,10 @@ EAF1FE9A29DB1A6000101452 /* Changeable.swift */, EAF1FE9829D4850E00101452 /* Clickable.swift */, EAA5EEDF28F49DB3003B3210 /* Colorable.swift */, + EAACB8972B92706F006A3869 /* DefaultValuing.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EAF978202A99035B00C2FEA9 /* Enabling.swift */, EA5E305929510F8B0082B959 /* EnumSubset.swift */, - EAACB8972B92706F006A3869 /* EnumValuing.swift */, EAF7F0A1289AFB3900B287F5 /* Errorable.swift */, EA3361AE288B26310071C351 /* FormFieldable.swift */, EA33624628931B050071C351 /* Initable.swift */, @@ -1007,7 +1007,7 @@ EAC9258C2911C9DE00091998 /* InputField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */, - EAACB8982B92706F006A3869 /* EnumValuing.swift in Sources */, + EAACB8982B92706F006A3869 /* DefaultValuing.swift in Sources */, EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */, EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */, 71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */, diff --git a/VDS/Protocols/EnumValuing.swift b/VDS/Protocols/DefaultValuing.swift similarity index 52% rename from VDS/Protocols/EnumValuing.swift rename to VDS/Protocols/DefaultValuing.swift index b8f27824..5f8bcb72 100644 --- a/VDS/Protocols/EnumValuing.swift +++ b/VDS/Protocols/DefaultValuing.swift @@ -7,6 +7,8 @@ import Foundation -public protocol EnumValuing: Valuing { +public protocol DefaultValuing: Valuing { static var defaultValue: Self { get } } + +public protocol CGFloatDefaultValuing: DefaultValuing where ValueType == CGFloat {} From 275a57d510b270119951ceb7ae0ba5c1d9bf771f Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:44:24 -0600 Subject: [PATCH 05/10] updated spacing Signed-off-by: Matt Bruce --- VDS/Extensions/VDSLayout.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/VDS/Extensions/VDSLayout.swift b/VDS/Extensions/VDSLayout.swift index 30df201b..26ada9c4 100644 --- a/VDS/Extensions/VDSLayout.swift +++ b/VDS/Extensions/VDSLayout.swift @@ -10,8 +10,7 @@ import Foundation /// Represents constants used that deal with layout. public struct VDSLayout { /// Enum used to describe the spacing constants. - public enum Spacing: String, CaseIterable, EnumValuing { - public static var defaultValue: VDSLayout.Spacing = .space2X + public enum Spacing: String, CaseIterable, Valuing { case space1X case space2X From 317173917f23d3738d29f0932c03826903ad00eb Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 1 Mar 2024 14:46:41 -0600 Subject: [PATCH 06/10] updated enums Signed-off-by: Matt Bruce --- VDS/Components/TileContainer/TileContainer.swift | 4 ++-- VDS/Components/Tilelet/Tilelet.swift | 2 +- VDS/Protocols/DefaultValuing.swift | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 27cfa1f4..6cf7d24f 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -14,7 +14,7 @@ import UIKit open class TileContainer: TileContainerBase { /// Enum used to describe the padding choices used for this component. - public enum Padding: EnumValuing { + public enum Padding: DefaultValuing { case padding2X case padding4X case padding6X @@ -43,7 +43,7 @@ open class TileContainer: TileContainerBase { } } -open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { +open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { //-------------------------------------------------- // MARK: - Initializers diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 77d6c70e..f8a3b4db 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -19,7 +19,7 @@ import Combine open class Tilelet: TileContainerBase { /// Enum used to describe the padding choices used for this component. - public enum Padding: String, EnumValuing, CaseIterable { + public enum Padding: String, DefaultValuing, CaseIterable { case small case large diff --git a/VDS/Protocols/DefaultValuing.swift b/VDS/Protocols/DefaultValuing.swift index 5f8bcb72..ffeaf0eb 100644 --- a/VDS/Protocols/DefaultValuing.swift +++ b/VDS/Protocols/DefaultValuing.swift @@ -10,5 +10,3 @@ import Foundation public protocol DefaultValuing: Valuing { static var defaultValue: Self { get } } - -public protocol CGFloatDefaultValuing: DefaultValuing where ValueType == CGFloat {} From fdd0d35f2b683f6c05dff5c3e50cecc202480a0f Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Fri, 15 Mar 2024 17:05:15 +0530 Subject: [PATCH 07/10] Added EyebrowModel for tilelet --- VDS.xcodeproj/project.pbxproj | 6 +- VDS/Components/Tilelet/Tilelet.swift | 20 ++++--- .../Tilelet/TileletSubTitleModel.swift | 3 - .../Tilelet/TiletEyebrowModel.swift | 55 +++++++++++++++++++ 4 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 VDS/Components/Tilelet/TiletEyebrowModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 1d773b71..a22c51b8 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; 710607952B91A99500F2863F /* TitleletChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 710607942B91A99500F2863F /* TitleletChangeLog.txt */; }; 7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; }; + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */; }; 71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; }; 71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; }; 71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */; }; @@ -190,6 +191,7 @@ 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; 710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = ""; }; 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = ""; }; + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiletEyebrowModel.swift; sourceTree = ""; }; 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = ""; }; 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = ""; }; 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceConfigurationValue.swift; sourceTree = ""; }; @@ -691,8 +693,9 @@ children = ( EA5E3057295105A40082B959 /* Tilelet.swift */, EA985BE529688F6A00F2FF2E /* TileletBadgeModel.swift */, - EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, + 71ACE89D2BA1CC1700FB6ADC /* TiletEyebrowModel.swift */, EA985BE72968951C00F2FF2E /* TileletTitleModel.swift */, + EA985BE929689B6D00F2FF2E /* TileletSubTitleModel.swift */, EA985C2C296F03FE00F2FF2E /* TileletIconModels.swift */, 710607942B91A99500F2863F /* TitleletChangeLog.txt */, ); @@ -1042,6 +1045,7 @@ EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, + 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index f8a3b4db..0f595343 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -250,6 +250,9 @@ open class Tilelet: TileContainerBase { /// If set, this is used to render the subTitleLabel of the TitleLockup. open var subTitleModel: SubTitleModel? { didSet { setNeedsUpdate() } } + /// If set, this is used to render the eyebrowLabel of the TitleLockup. + open var eyebrowModel: EyebrowModel? { didSet { setNeedsUpdate() } } + //only 1 Icon can be active private var _descriptiveIconModel: DescriptiveIcon? @@ -320,7 +323,7 @@ open class Tilelet: TileContainerBase { titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) - + iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(directionalIcon) @@ -335,7 +338,7 @@ open class Tilelet: TileContainerBase { .pinBottom() //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). - var labelPriority = UILayoutPriority.defaultHigh.rawValue + let labelPriority = UILayoutPriority.defaultHigh.rawValue titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) @@ -400,7 +403,11 @@ open class Tilelet: TileContainerBase { private func updateTitleLockup() { var showTitleLockup = false - + + if let eyebrowModel, !eyebrowModel.text.isEmpty { + showTitleLockup = true + } + if let titleModel, !titleModel.text.isEmpty { showTitleLockup = true } @@ -444,6 +451,7 @@ open class Tilelet: TileContainerBase { } //set models + titleLockup.eyebrowModel = eyebrowModel?.toTitleLockupEyebrowModel() titleLockup.titleModel = titleModel?.toTitleLockupTitleModel() titleLockup.subTitleModel = subTitleModel?.toTitleLockupSubTitleModel() @@ -495,12 +503,6 @@ open class Tilelet: TileContainerBase { } else { removeFromSuperview(iconContainerView) } - if let lastElement = titleLockup.lastElement { - titleLockupTitleLabelBottomConstraint?.deactivate() - let anchorConstraint = showIconContainerView ? iconContainerView.topAnchor : containerView.bottomAnchor - titleLockupTitleLabelBottomConstraint = anchorConstraint.constraint(greaterThanOrEqualTo: lastElement.bottomAnchor) - titleLockupTitleLabelBottomConstraint?.activate() - } } private func updateTextPositionAlignment() { diff --git a/VDS/Components/Tilelet/TileletSubTitleModel.swift b/VDS/Components/Tilelet/TileletSubTitleModel.swift index 62adda7e..fb68ff53 100644 --- a/VDS/Components/Tilelet/TileletSubTitleModel.swift +++ b/VDS/Components/Tilelet/TileletSubTitleModel.swift @@ -48,14 +48,11 @@ extension Tilelet { otherStandardStyle: OtherStandardStyle = .bodySmall, textColor: Use = .primary, textAttributes: [any LabelAttributeModel]? = nil, - standardStyle: StandardStyle = .bodySmall, lineBreakMode: NSLineBreakMode = .byTruncatingTail) { - textAttributes: [any LabelAttributeModel]? = nil) { self.text = text self.otherStandardStyle = otherStandardStyle self.textAttributes = textAttributes self.textColor = textColor - self.standardStyle = standardStyle self.lineBreakMode = lineBreakMode } diff --git a/VDS/Components/Tilelet/TiletEyebrowModel.swift b/VDS/Components/Tilelet/TiletEyebrowModel.swift new file mode 100644 index 00000000..e7c008b4 --- /dev/null +++ b/VDS/Components/Tilelet/TiletEyebrowModel.swift @@ -0,0 +1,55 @@ +// +// TiletEyebrowModel.swift +// VDS +// +// Created by Bandaru, Krishna Kishore on 13/03/24. +// + +import Foundation +import UIKit + +extension Tilelet { + + /// Model that represents the options available for the eyebrow label. + public struct EyebrowModel { + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Text that will be used for the eyebrow label. + public var text: String = "" + + /// Used in combination with standardStyle to set the textStyle that will be used for the eyebrow label. + public var isBold: Bool = false + /// Text attributes that will be used for the eyebrow label. + public var textAttributes: [any LabelAttributeModel]? + + /// Text style that will be used for the eyebrow label. If subtitle standard style + public var standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .titleSmall + + /// LineBreakMode used in Badge label. + public var lineBreakMode: NSLineBreakMode + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init(text: String, + textAttributes: [any LabelAttributeModel]? = nil, + isBold: Bool = true, + standardStyle: Tilelet.SubTitleModel.OtherStandardStyle = .bodySmall, + lineBreakMode: NSLineBreakMode = .byTruncatingTail) { + self.text = text + self.textAttributes = textAttributes + self.standardStyle = standardStyle + self.isBold = isBold + self.lineBreakMode = lineBreakMode + } + + //-------------------------------------------------- + // MARK: - Public Methods + //-------------------------------------------------- + /// Converts this type of model to a TitleLockup.TitleModel. + public func toTitleLockupEyebrowModel() -> TitleLockup.EyebrowModel { + TitleLockup.EyebrowModel(text: text, isBold: isBold, standardStyle: standardStyle.value, textAttributes: textAttributes) + } + } +} From baf934ba8da4cb8ff25c1290b927991cf0d94bca Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Tue, 19 Mar 2024 18:44:25 +0530 Subject: [PATCH 08/10] added Truncation logic in tilelet --- VDS/Components/Tilelet/Tilelet.swift | 58 ++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 0f595343..b1367736 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -289,6 +289,11 @@ open class Tilelet: TileContainerBase { internal var titleLockupBottomGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupCenterYConstraint: NSLayoutConstraint? internal var titleLockupTitleLabelBottomConstraint: NSLayoutConstraint? + //Truncation constraints + internal var badgeLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupEyebrowLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? + internal var titleLockupSubTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -319,7 +324,7 @@ open class Tilelet: TileContainerBase { titleLockupBottomConstraint = titleLockupContainerView.bottomAnchor.constraint(equalTo: titleLockup.bottomAnchor) titleLockupBottomConstraint?.activate() titleLockupTrailingConstraint = titleLockup.trailingAnchor.constraint(equalTo: titleLockupContainerView.trailingAnchor) - titleLockupTrailingConstraint?.isActive = true + titleLockupTrailingConstraint?.activate() titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) @@ -337,11 +342,46 @@ open class Tilelet: TileContainerBase { .pinTop() .pinBottom() - //If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + badge.bottomAnchor.constraint(equalTo: badge.label.bottomAnchor, constant: 2).activate() + + /** + Truncation: + If a Tilelet has only a Title or a Subtitle, then the Title or Subtitle is truncated and appended with an ellipsis when there is not enough space to display the full text. + If a Tilelet has both Title and Subtitle, then only Subtitle will be truncated. + If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + Minimum bottom space below Badge is 4px; less than 4px results in truncation. + */ let labelPriority = UILayoutPriority.defaultHigh.rawValue titleLockup.titleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority), for: .vertical) badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) + titleLockup.eyebrowLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-3), for: .vertical) + + titleLockup.titleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority), for: .vertical) + badge.label.setContentHuggingPriority(UILayoutPriority(labelPriority-1), for: .vertical) + titleLockup.subTitleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-2), for: .vertical) + titleLockup.eyebrowLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-3), for: .vertical) + + /** + Added these constraints for: + At fixed width & height if all the labels(Badge, Eyebrow, Title, Subtitle) are having more number of lines then we should display atleast one line of content per label instead of pushing labels out of bounds. + So adding minimum single line height constraint + */ + badgeLabelHeightGreaterThanConstraint = badge.label.heightGreaterThanEqualTo(constant: badge.label.minimumLineHeight) + badgeLabelHeightGreaterThanConstraint?.priority = .defaultHigh + badgeLabelHeightGreaterThanConstraint?.activate() + + titleLockupEyebrowLabelHeightGreaterThanConstraint = titleLockup.eyebrowLabel.heightGreaterThanEqualTo(constant: titleLockup.eyebrowLabel.minimumLineHeight) + titleLockupEyebrowLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupEyebrowLabelHeightGreaterThanConstraint?.activate() + + titleLockupTitleLabelHeightGreaterThanConstraint = titleLockup.titleLabel.heightGreaterThanEqualTo(constant: titleLockup.titleLabel.minimumLineHeight) + titleLockupTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupTitleLabelHeightGreaterThanConstraint?.activate() + + titleLockupSubTitleLabelHeightGreaterThanConstraint = titleLockup.subTitleLabel.heightGreaterThanEqualTo(constant: titleLockup.subTitleLabel.minimumLineHeight) + titleLockupSubTitleLabelHeightGreaterThanConstraint?.priority = .defaultHigh + titleLockupSubTitleLabelHeightGreaterThanConstraint?.activate() } /// Resets to default settings. @@ -391,6 +431,7 @@ open class Tilelet: TileContainerBase { badge.numberOfLines = badgeModel.numberOfLines badge.surface = badgeModel.surface badge.maxWidth = badgeModel.maxWidth + badgeLabelHeightGreaterThanConstraint?.constant = badge.label.minimumLineHeight if badgeContainerView.superview == nil { stackView.insertArrangedSubview(badgeContainerView, at: 0) setNeedsLayout() @@ -462,6 +503,9 @@ open class Tilelet: TileContainerBase { } else { removeFromSuperview(titleLockupContainerView) } + titleLockupEyebrowLabelHeightGreaterThanConstraint?.constant = titleLockup.eyebrowLabel.minimumLineHeight + titleLockupTitleLabelHeightGreaterThanConstraint?.constant = titleLockup.titleLabel.minimumLineHeight + titleLockupSubTitleLabelHeightGreaterThanConstraint?.constant = titleLockup.subTitleLabel.minimumLineHeight } private func updateIcons() { @@ -529,11 +573,11 @@ open class Tilelet: TileContainerBase { } } -extension TitleLockup { +extension Label { - fileprivate var lastElement: UIView? { - guard subTitleModel == nil else { return subTitleLabel } - guard titleModel == nil else { return titleLabel } - return nil + ///To calculate label single line height + fileprivate var minimumLineHeight: CGFloat { + guard let text, !text.isEmpty, let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: attributedText?.string.count ?? 0))[.paragraphStyle] as? NSParagraphStyle else { return font.lineHeight } + return attributes.minimumLineHeight } } From f528b3cd62303f3f9c3a35be7d6a8b7e74763be1 Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Wed, 20 Mar 2024 20:54:19 +0530 Subject: [PATCH 09/10] changed constraints for backgroundImageView and updated frames for CAlayers --- .../TileContainer/TileContainer.swift | 17 +++++++++++++++-- VDS/Components/Tilelet/Tilelet.swift | 6 ++---- VDS/Protocols/DropShadowable.swift | 5 ++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 14d38d05..fc0ffa6e 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -240,7 +240,12 @@ open class TileContainerBase: Control where Padding heightConstraint = layoutGuide.heightAnchor.constraint(equalToConstant: 0) - backgroundImageView.pin(layoutGuide) + backgroundImageView + .pinTop(layoutGuide.topAnchor) + .pinLeading(layoutGuide.leadingAnchor) + .pinTrailing(layoutGuide.trailingAnchor) + .pinBottom(layoutGuide.bottomAnchor, 0, .dragThatCanResizeScene) + backgroundImageView.isUserInteractionEnabled = false backgroundImageView.isHidden = true @@ -257,6 +262,7 @@ open class TileContainerBase: Control where Padding layer.cornerRadius = cornerRadius backgroundImageView.layer.cornerRadius = cornerRadius highlightView.layer.cornerRadius = cornerRadius + clipsToBounds = true } /// Resets to default settings. @@ -327,7 +333,14 @@ open class TileContainerBase: Control where Padding } applyBackgroundEffects() } - + + /// Used to update frames for the added CAlayers to our view + open override func layoutSubviews() { + super.layoutSubviews() + dropShadowLayers?.forEach { $0.frame = bounds } + gradientLayers?.forEach { $0.frame = bounds } + } + //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index b1367736..fca3b4c2 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -349,6 +349,7 @@ open class Tilelet: TileContainerBase { If a Tilelet has only a Title or a Subtitle, then the Title or Subtitle is truncated and appended with an ellipsis when there is not enough space to display the full text. If a Tilelet has both Title and Subtitle, then only Subtitle will be truncated. If a Tilelet has Badge, Title and Subtitle, then Subtitle will be truncated first and Badge will be truncated second. Title will be truncated last (lowest priority). + Atleast one line text based on priority Minimum bottom space below Badge is 4px; less than 4px results in truncation. */ let labelPriority = UILayoutPriority.defaultHigh.rawValue @@ -576,8 +577,5 @@ open class Tilelet: TileContainerBase { extension Label { ///To calculate label single line height - fileprivate var minimumLineHeight: CGFloat { - guard let text, !text.isEmpty, let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: attributedText?.string.count ?? 0))[.paragraphStyle] as? NSParagraphStyle else { return font.lineHeight } - return attributes.minimumLineHeight - } + fileprivate var minimumLineHeight: CGFloat { textStyle.lineHeight } } diff --git a/VDS/Protocols/DropShadowable.swift b/VDS/Protocols/DropShadowable.swift index b569ecab..120abb98 100644 --- a/VDS/Protocols/DropShadowable.swift +++ b/VDS/Protocols/DropShadowable.swift @@ -50,7 +50,6 @@ extension ViewProtocol where Self: UIView { shadowLayer.shadowPath = shadowPath.cgPath shadowLayer.frame = bounds shadowLayer.position = .init(x: bounds.midX, y: bounds.midY) - shadowLayer.backgroundColor = backgroundColor?.cgColor shadowLayer.cornerRadius = layer.cornerRadius shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor shadowLayer.shadowOpacity = Float(config.shadowOpacityConfiguration.value(for: self)) @@ -86,4 +85,8 @@ extension ViewProtocol where Self: UIView { func removeGradientLayer() { layer.sublayers?.removeAll { $0.name == "gradientLayer" } } + + //Helper variables to get gradient & shadow layers. These are exposed to update frame of layers when view's frame is updated. + var dropShadowLayers: [CALayer]? { layer.sublayers?.filter { $0.name == "dropShadowLayer" } } + var gradientLayers: [CAGradientLayer]? { layer.sublayers?.filter { $0.name == "gradientLayer" } as? [CAGradientLayer] } } From f8fec46805fc8b695700d4be7caa35a9a388cf0c Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Thu, 21 Mar 2024 16:25:51 +0530 Subject: [PATCH 10/10] iPad bug fix for imageview --- VDS/Components/TileContainer/TileContainer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index fc0ffa6e..d94d942f 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -244,7 +244,7 @@ open class TileContainerBase: Control where Padding .pinTop(layoutGuide.topAnchor) .pinLeading(layoutGuide.leadingAnchor) .pinTrailing(layoutGuide.trailingAnchor) - .pinBottom(layoutGuide.bottomAnchor, 0, .dragThatCanResizeScene) + .pinBottom(layoutGuide.bottomAnchor, 0, .defaultLow) backgroundImageView.isUserInteractionEnabled = false backgroundImageView.isHidden = true