Merge branch 'develop' into feature/tileletEnhancements

This commit is contained in:
Krishna Kishore Bandaru 2024-03-18 12:28:17 +05:30
commit 56135feef9
14 changed files with 207 additions and 79 deletions

View File

@ -136,6 +136,7 @@
EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068912A560B65002E3A2D /* LoaderViewController.swift */; }; EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068912A560B65002E3A2D /* LoaderViewController.swift */; };
EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */; }; EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */; };
EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; };
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE785302BA0A438009428EA /* UIImage+Helper.swift */; };
EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */; }; EAEEEC922B1F807300531FC2 /* BadgeChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */; };
EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */; }; EAEEEC962B1F893B00531FC2 /* ButtonChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */; };
EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */; }; EAEEEC982B1F8DD100531FC2 /* LineChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */; };
@ -311,6 +312,7 @@
EAD068912A560B65002E3A2D /* LoaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderViewController.swift; sourceTree = "<group>"; }; EAD068912A560B65002E3A2D /* LoaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderViewController.swift; sourceTree = "<group>"; };
EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderLaunchable.swift; sourceTree = "<group>"; }; EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderLaunchable.swift; sourceTree = "<group>"; };
EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = "<group>"; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = "<group>"; };
EAE785302BA0A438009428EA /* UIImage+Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helper.swift"; sourceTree = "<group>"; };
EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BadgeChangeLog.txt; sourceTree = "<group>"; }; EAEEEC912B1F807300531FC2 /* BadgeChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = BadgeChangeLog.txt; sourceTree = "<group>"; };
EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonChangeLog.txt; sourceTree = "<group>"; }; EAEEEC952B1F893B00531FC2 /* ButtonChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonChangeLog.txt; sourceTree = "<group>"; };
EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LineChangeLog.txt; sourceTree = "<group>"; }; EAEEEC972B1F8DD100531FC2 /* LineChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LineChangeLog.txt; sourceTree = "<group>"; };
@ -544,6 +546,7 @@
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */, EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */,
EA33623D2892EE950071C351 /* UIDevice.swift */, EA33623D2892EE950071C351 /* UIDevice.swift */,
EA0B18092AA78F9000F2D0CD /* UIEdgeInsets.swift */, EA0B18092AA78F9000F2D0CD /* UIEdgeInsets.swift */,
EAE785302BA0A438009428EA /* UIImage+Helper.swift */,
EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */, EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */,
EA8E40902A7D3F6300934ED3 /* UIView+Accessibility.swift */, EA8E40902A7D3F6300934ED3 /* UIView+Accessibility.swift */,
EAB5FED329267EB300998C17 /* UIView+NSLayoutConstraint.swift */, EAB5FED329267EB300998C17 /* UIView+NSLayoutConstraint.swift */,
@ -1040,6 +1043,7 @@
EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */,
EAC925842911C63100091998 /* Colorable.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */,
EAACB89A2B927108006A3869 /* Valuing.swift in Sources */, EAACB89A2B927108006A3869 /* Valuing.swift in Sources */,
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */,
EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */, EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */,
EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */,
EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */, EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */,

View File

@ -102,7 +102,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
/// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes. /// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
open var labelAttributedText: NSAttributedString? { open var labelAttributedText: NSAttributedString? {
didSet { didSet {
label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true)
label.attributedText = labelAttributedText label.attributedText = labelAttributedText
setNeedsUpdate() setNeedsUpdate()
} }
@ -117,7 +116,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
/// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes. /// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
open var childAttributedText: NSAttributedString? { open var childAttributedText: NSAttributedString? {
didSet { didSet {
childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true)
childLabel.attributedText = childAttributedText childLabel.attributedText = childAttributedText
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -34,10 +34,17 @@ public final class SelfSizingCollectionView: UICollectionView {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
private var contentSizeObservation: NSKeyValueObservation?
private var collectionViewHeight: NSLayoutConstraint? private var collectionViewHeight: NSLayoutConstraint?
private var anyCancellable: AnyCancellable? private var anyCancellable: AnyCancellable?
private var contentSizeSubject = CurrentValueSubject<CGSize, Never>(.zero)
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var contentSizePublisher: AnyPublisher<CGSize, Never> {
contentSizeSubject.eraseToAnyPublisher()
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
@ -45,7 +52,6 @@ public final class SelfSizingCollectionView: UICollectionView {
/// The natural size for the receiving view, considering only properties of the view itself. /// The natural size for the receiving view, considering only properties of the view itself.
public override var intrinsicContentSize: CGSize { public override var intrinsicContentSize: CGSize {
let contentSize = self.contentSize let contentSize = self.contentSize
//print(#function, contentSize)
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
} }
@ -67,18 +73,17 @@ public final class SelfSizingCollectionView: UICollectionView {
//ensure autoLayout uses intrinsic height //ensure autoLayout uses intrinsic height
setContentHuggingPriority(.required, for: .vertical) setContentHuggingPriority(.required, for: .vertical)
setContentCompressionResistancePriority(.required, for: .vertical) setContentCompressionResistancePriority(.required, for: .vertical)
collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate() collectionViewHeight = height(constant: 0, priority: .defaultHigh)
// Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews. anyCancellable = self.publisher(for: \.contentSize, options: [.new])
self.contentSizeObservation = self.observe(\.contentSize, options: [.old, .new]) { [weak self] _, change in .sink { [weak self] compare in
// If we don't specify `options: [.old, .new]`, the change.oldValue and .newValue will always be `nil`. guard let self else { return }
if change.newValue != change.oldValue { if compare.height != self.collectionViewHeight?.constant {
self?.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
if let height = change.newValue?.height { self.collectionViewHeight?.constant = compare.height
self?.collectionViewHeight?.constant = height self.contentSizeSubject.send(compare)
} }
} }
}
} }
} }

View File

@ -102,6 +102,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
//-------------------------------------------------- //--------------------------------------------------
open func initialSetup() { open func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true
backgroundColor = .clear backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = [] accessibilityCustomActions = []

View File

@ -9,6 +9,7 @@ import Foundation
import UIKit import UIKit
import VDSColorTokens import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine
/// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings. /// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings.
@objc(VDSButtonGroup) @objc(VDSButtonGroup)
@ -98,6 +99,8 @@ open class ButtonGroup: View {
buttons.forEach { $0.surface = surface } buttons.forEach { $0.surface = surface }
} }
} }
open var contentSizePublisher: AnyPublisher<CGSize, Never> { collectionView.contentSizePublisher }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
@ -108,6 +111,7 @@ open class ButtonGroup: View {
$0.delegate = self $0.delegate = self
} }
/// CollectionView that renders the array of buttonBase obects.
fileprivate lazy var collectionView: SelfSizingCollectionView = { fileprivate lazy var collectionView: SelfSizingCollectionView = {
return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with { return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with {

View File

@ -44,7 +44,7 @@ open class Icon: View {
//-------------------------------------------------- //--------------------------------------------------
/// UIImageView used to render the icon. /// UIImageView used to render the icon.
open var imageView = UIImageView().with { open var imageView = UIImageView().with {
$0.isAccessibilityElement = false $0.isAccessibilityElement = false
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
$0.contentMode = .scaleAspectFill $0.contentMode = .scaleAspectFill
$0.clipsToBounds = true $0.clipsToBounds = true
@ -109,8 +109,8 @@ open class Icon: View {
//get the image name //get the image name
//set the image //set the image
if let name, let image = getImage(for: name.rawValue) { if let name, let image = UIImage.image(for: name, color: imageColor) {
setImage(image: image, imageColor: imageColor) imageView.image = image
} else { } else {
imageView.image = nil imageView.image = nil
} }
@ -129,17 +129,17 @@ open class Icon: View {
super.updateAccessibility() super.updateAccessibility()
accessibilityLabel = name?.rawValue ?? "icon" accessibilityLabel = name?.rawValue ?? "icon"
} }
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
private func getImage(for imageName: String) -> UIImage? {
return BundleManager.shared.image(for: imageName)
}
private func setImage(image: UIImage, imageColor: UIColor) {
imageView.image = image.withTintColor(imageColor)
}
} }
extension UIImage {
/// UIImage helper for finding images based on the Icon.Name which uses the internal BundleManager.
/// - Parameters:
/// - name: Icon.Name rawRepresentable.
/// - color: Color to Tint the image with
/// - renderingMode: UIImage Rendering mode.
/// - Returns: UIImage for this proecess
public static func image(for iconName: Icon.Name, color: UIColor? = nil, renderingMode: UIImage.RenderingMode = .alwaysOriginal) -> UIImage? {
image(representing: iconName, color: color, renderingMode: renderingMode)
}
}

View File

@ -42,6 +42,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
private enum TextSetMode {
case text
case attributedText
}
private var textSetMode: TextSetMode = .text
private var initialSetupPerformed = false private var initialSetupPerformed = false
private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets } private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets }
@ -101,18 +108,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
/// Key of whether or not updateView() is called in setNeedsUpdate() /// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true open var shouldUpdateView: Bool = true
/// Determines if the label should use its own attributedText property instead of rendering the attributedText propert
/// based of other local properties, such as textStyle, textColor, surface, etc... The default value is false.
open var useAttributedText: Bool = false
/// Will determine if a scaled font should be used for the font. /// Will determine if a scaled font should be used for the font.
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }} open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
open var surface: Surface = .light { didSet { setNeedsUpdate() }} open var surface: Surface = .light { didSet { setNeedsUpdate() }}
/// Array of LabelAttributeModel objects used in rendering the text. /// Array of LabelAttributeModel objects used in rendering the text.
open var attributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }} open var attributes: [any LabelAttributeModel]? { didSet { setNeedsUpdate() }}
/// TextStyle used on the this label. /// TextStyle used on the this label.
@ -128,19 +131,40 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
/// Line break mode for the label, default is set to word wrapping. /// Line break mode for the label, default is set to word wrapping.
open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }} open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }}
private var _text: String?
/// Text that will be used in the label. /// Text that will be used in the label.
override open var text: String? { private var _text: String!
get { _text } override open var text: String! {
set { didSet {
if _text != newValue || newValue != attributedText?.string { _text = text
_text = newValue textSetMode = .text
useAttributedText = false setNeedsUpdate()
attributes?.removeAll() }
setNeedsUpdate() }
///AttributedText that will be used in the label.
override open var attributedText: NSAttributedString? {
didSet {
textSetMode = .attributedText
setNeedsUpdate()
}
}
override open var font: UIFont! {
didSet {
if let font, initialSetupPerformed {
textStyle = TextStyle.convert(font: font)
} }
setNeedsUpdate()
}
}
override open var textColor: UIColor! {
didSet {
if let textColor, initialSetupPerformed {
textColorConfiguration = SurfaceColorConfiguration(textColor, textColor).eraseToAnyColorable()
}
setNeedsUpdate()
} }
} }
@ -162,13 +186,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
open func initialSetup() { open func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true
//register for ContentSizeChanges //register for ContentSizeChanges
NotificationCenter NotificationCenter
.Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification) .Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification)
.sink { [weak self] notification in .sink { [weak self] notification in
self?.setNeedsUpdate() self?.setNeedsUpdate()
}.store(in: &subscribers) }.store(in: &subscribers)
backgroundColor = .clear backgroundColor = .clear
numberOfLines = 0 numberOfLines = 0
lineBreakMode = .byWordWrapping lineBreakMode = .byWordWrapping
@ -200,30 +224,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
} }
open func updateView() { open func updateView() {
if !useAttributedText { restyleText()
if let text {
accessibilityCustomActions = [] //force a drawText
setNeedsDisplay()
//create the primary string
let mutableText = NSMutableAttributedString.mutableText(for: text, setNeedsLayout()
textStyle: textStyle, layoutIfNeeded()
useScaledFont: useScaledFont,
textColor: textColorConfiguration.getColor(self),
alignment: textAlignment,
lineBreakMode: lineBreakMode)
applyAttributes(mutableText)
//set the attributed text
attributedText = mutableText
//force a drawText
setNeedsDisplay()
setNeedsLayout()
layoutIfNeeded()
}
}
} }
open func updateAccessibility() { open func updateAccessibility() {
@ -269,6 +276,55 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Methods // MARK: - Private Methods
//-------------------------------------------------- //--------------------------------------------------
private func restyleText() {
if textSetMode == .text {
styleText(_text)
} else {
styleAttributedText(attributedText)
}
}
private func styleText(_ newValue: String!) {
defer { invalidateIntrinsicContentSize() }
guard let newValue, !newValue.isEmpty else {
// We don't need to use attributed text
super.attributedText = nil
super.text = newValue
return
}
accessibilityCustomActions = []
//create the primary string
let mutableText = NSMutableAttributedString.mutableText(for: newValue,
textStyle: textStyle,
useScaledFont: useScaledFont,
textColor: textColorConfiguration.getColor(self),
alignment: textAlignment,
lineBreakMode: lineBreakMode)
applyAttributes(mutableText)
// Set attributed text to match typography
super.attributedText = mutableText
}
private func styleAttributedText(_ newValue: NSAttributedString?) {
defer { invalidateIntrinsicContentSize() }
guard let newValue, !newValue.string.isEmpty else {
// We don't need any additional styling
super.attributedText = newValue
return
}
let mutableText = NSMutableAttributedString(attributedString: newValue)
applyAttributes(mutableText)
// Modify attributed text to match typography
super.attributedText = mutableText
}
private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) { private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) {
actions = [] actions = []
@ -366,3 +422,5 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
} }
} }
} }

View File

@ -100,7 +100,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
/// If provided, the RadioBox textAttributedText will be rendered. /// If provided, the RadioBox textAttributedText will be rendered.
open var textAttributedText: NSAttributedString? { open var textAttributedText: NSAttributedString? {
didSet { didSet {
textLabel.useAttributedText = !(textAttributedText?.string.isEmpty ?? true)
textLabel.attributedText = textAttributedText textLabel.attributedText = textAttributedText
setNeedsUpdate() setNeedsUpdate()
} }
@ -115,7 +114,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
/// If provided, the RadioBox subTextAttributedText will be rendered. /// If provided, the RadioBox subTextAttributedText will be rendered.
open var subTextAttributedText: NSAttributedString? { open var subTextAttributedText: NSAttributedString? {
didSet { didSet {
subTextLabel.useAttributedText = !(subTextAttributedText?.string.isEmpty ?? true)
subTextLabel.attributedText = subTextAttributedText subTextLabel.attributedText = subTextAttributedText
setNeedsUpdate() setNeedsUpdate()
} }
@ -130,7 +128,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
/// If provided, the RadioBox subTextRightAttributedText will be rendered. /// If provided, the RadioBox subTextRightAttributedText will be rendered.
open var subTextRightAttributedText: NSAttributedString? { open var subTextRightAttributedText: NSAttributedString? {
didSet { didSet {
subTextRightLabel.useAttributedText = !(subTextRightAttributedText?.string.isEmpty ?? true)
subTextRightLabel.attributedText = subTextRightAttributedText subTextRightLabel.attributedText = subTextRightAttributedText
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -94,6 +94,7 @@ open class TextView: UITextView, ViewProtocol {
//-------------------------------------------------- //--------------------------------------------------
open func initialSetup() { open func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true
backgroundColor = .clear backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = [] accessibilityCustomActions = []

View File

@ -68,6 +68,7 @@ open class TitleLockup: View {
/// Label used to render the eyebrow model. /// Label used to render the eyebrow model.
open var eyebrowLabel = Label().with { open var eyebrowLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentHuggingPriority(.required, for: .vertical)
} }
/// Model used in rendering the eyebrow label. /// Model used in rendering the eyebrow label.
@ -77,6 +78,7 @@ open class TitleLockup: View {
/// Label used to render the title model. /// Label used to render the title model.
open var titleLabel = Label().with { open var titleLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentHuggingPriority(.required, for: .vertical)
$0.accessibilityTraits.insert([.header]) $0.accessibilityTraits.insert([.header])
} }
@ -87,6 +89,7 @@ open class TitleLockup: View {
/// Label used to render the subtitle model. /// Label used to render the subtitle model.
open var subTitleLabel = Label().with { open var subTitleLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentHuggingPriority(.required, for: .vertical)
} }
/// Model used in rendering the subtitle label. /// Model used in rendering the subtitle label.
@ -380,7 +383,7 @@ open class TitleLockup: View {
} }
//pin the last view to the bottom of this view //pin the last view to the bottom of this view
previousView?.pinBottom(0, .defaultHigh) previousView?.pinBottom(0)
//debugging for borders //debugging for borders
eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green) eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green)

View File

@ -0,0 +1,26 @@
//
// UIImage+Icon.swift
// VDS
//
// Created by Matt Bruce on 3/11/24.
//
import Foundation
import UIKit
extension UIImage {
/// UIImage helper for finding images based on the Icon.Name which uses the internal BundleManager.
/// - Parameters:
/// - name: RawRepresentable.
/// - color: Color to Tint the image with
/// - renderingMode: UIImage Rendering mode.
/// - Returns: UIImage for this proecess
public static func image<T: RawRepresentable>(representing representable: T, color: UIColor? = nil, renderingMode: UIImage.RenderingMode = .alwaysOriginal) -> UIImage? where T.RawValue == String {
guard let image = BundleManager.shared.image(for: representable.rawValue) else { return nil }
guard let color else { return image }
return image.withTintColor(color, renderingMode: renderingMode)
}
}

View File

@ -6,15 +6,17 @@
// //
import Foundation import Foundation
import UIKit
/// Enum that is matched up for the Verizon fonts. /// Enum that is matched up for the Verizon fonts.
public enum Font: String, FontProtocol { public enum Font: FontProtocol {
case edsBold case edsBold
case edsRegular case edsRegular
case dsLight case dsLight
case etxBold case etxBold
case etxRegular case etxRegular
case custom(UIFont)
public var fontName: String { public var fontName: String {
switch self { switch self {
case .edsBold: case .edsBold:
@ -27,11 +29,32 @@ public enum Font: String, FontProtocol {
return "VerizonNHGeTX-Bold" return "VerizonNHGeTX-Bold"
case .etxRegular: case .etxRegular:
return "VerizonNHGeTX-Regular" return "VerizonNHGeTX-Regular"
case .custom(let font):
return font.fontName
} }
} }
public static var allCases: [Font] {
[.edsBold, .edsRegular, .dsLight, .etxBold, .etxRegular]
}
/// File Extension for each of the Font enums. /// File Extension for each of the Font enums.
public var fontFileExtension: String { public var fontFileExtension: String {
return "otf" return "otf"
} }
/// Returns a UIFont for the fontName and size given.
/// - Parameters:
/// - size: Size of the font
/// - Returns: UIFont for the fontName and Size.
public func font(ofSize size: CGFloat) -> UIFont{
DispatchQueue.once(block: { self.register() })
switch self {
case .custom(let font):
return font
default:
guard let found = UIFont(name: self.fontName, size: size) else { return .systemFont(ofSize: size) }
return found
}
}
} }

View File

@ -9,9 +9,10 @@ import Foundation
import UIKit import UIKit
/// Used in Classes that require Fonts /// Used in Classes that require Fonts
public protocol FontProtocol: CaseIterable, RawRepresentable, Hashable { public protocol FontProtocol {
var fontFileExtension: String { get } var fontFileExtension: String { get }
var fontName: String { get } var fontName: String { get }
static var allCases: [Self] { get }
} }
extension FontProtocol { extension FontProtocol {

View File

@ -209,6 +209,13 @@ extension TextStyle {
boldMicro boldMicro
] ]
} }
public static func convert(font: UIFont) -> TextStyle {
guard let found = allCases.first(where: { font.fontName == $0.fontFace.fontName && font.pointSize == $0.pointSize} ) else {
return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize)
}
return found
}
} }
extension TextStyle { extension TextStyle {